context-loader 1.3.0__tar.gz → 1.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: context-loader
3
- Version: 1.3.0
3
+ Version: 1.3.2
4
4
  Summary: Render deterministic local Git context for any executor
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -219,14 +219,14 @@ Python standard library.
219
219
 
220
220
  ## Open-source quick start
221
221
 
222
- Current stable release: **1.3.0**. Local CLI for coding-agent workflows; renders deterministic Markdown or JSON.
222
+ Current stable release: **1.3.2**. Local CLI for coding-agent workflows; renders deterministic Markdown or JSON.
223
223
 
224
224
  ```bash
225
- uv tool install 'context-loader==1.3.0'
225
+ uv tool install 'context-loader==1.3.2'
226
226
  ```
227
227
 
228
228
  ```bash
229
- pip install 'context-loader==1.3.0'
229
+ pip install 'context-loader==1.3.2'
230
230
  ```
231
231
 
232
232
  ```bash
@@ -239,21 +239,21 @@ project-context --repo /path/to/repo --format json
239
239
 
240
240
  ## Install
241
241
 
242
- Source version: `1.3.0`. Install its matching published release or an exact source commit.
242
+ Source version: `1.3.2`. Install its matching published release or an exact source commit.
243
243
 
244
244
  Install via `uv`:
245
245
 
246
246
  ```bash
247
- uv tool install 'context-loader==1.3.0'
247
+ uv tool install 'context-loader==1.3.2'
248
248
  ```
249
249
 
250
250
  Install via `pip`:
251
251
 
252
252
  ```bash
253
- pip install 'context-loader==1.3.0'
253
+ pip install 'context-loader==1.3.2'
254
254
  ```
255
255
 
256
- For development or source-based installs tracking current repository (`1.3.0`):
256
+ For development or source-based installs tracking current repository (`1.3.2`):
257
257
 
258
258
  ```bash
259
259
  uv tool install git+https://github.com/xuanheng-tech/context-loader.git
@@ -319,6 +319,8 @@ and bare repositories are rejected in every mode.
319
319
  `--focus` and `--path` are optional, bounded selection signals for the root `AGENTS.md`. `--path`
320
320
  must be repository-relative. The collector does not retain either input in output or audit data.
321
321
  Calls that omit both options remain valid and use the conservative fallback described below.
322
+ Each value option (`--repo`, `--focus`, `--path`, `--format`) may be supplied once;
323
+ duplicates are rejected before collection rather than silently overriding an earlier value.
322
324
 
323
325
  ## Markdown Output
324
326
 
@@ -338,16 +340,17 @@ The output has no generated timestamp, AI summary, architecture inference, or di
338
340
  ## JSON Output
339
341
 
340
342
  `--format json` writes exactly one separator-compact UTF-8 JSON document plus one trailing newline
341
- to stdout. Keys are serialized in sorted order with `ensure_ascii=False`. `--format json-compact`
342
- uses the identical serialization; only the field set differs, as described under “Compact model
343
- consumption”. The declared contract for `--format json` is:
343
+ to stdout, within the JSON document bound under “Limits”. Keys are serialized in sorted order
344
+ with `ensure_ascii=False`. `--format json-compact` uses the identical serialization; only the field
345
+ set differs, as described under “Compact model consumption”. The declared contract for
346
+ `--format json` is:
344
347
 
345
348
  ```json
346
349
  {
347
350
  "schema_version": 3,
348
351
  "tool": {
349
352
  "name": "context-loader",
350
- "version": "1.3.0"
353
+ "version": "1.3.2"
351
354
  },
352
355
  "repository": {
353
356
  "requested_path": "/canonical/requested/path",
@@ -394,12 +397,12 @@ context, in assembly order, after the existing newline normalization and truncat
394
397
  distinguishes `repository` from `global`; version 1.0.0's fixed root-file selection currently emits
395
398
  only `repository` sources and does not add any global-file discovery.
396
399
 
397
- `statuses` lists machine-readable collection and render conditions that previously appeared only inside `context` Markdown — plus the nested-context truncation flags, which exist only in machine form: skipped or absent sources, truncated sources, unreadable directory-tree entries, sections omitted under the global output budget, truncated working-tree or declared-command listings, and truncated nested-`AGENTS.md` presence reports. Each entry has stable `code`, `subject_kind`, and `subject` fields. The two `nested_agents_*` codes mirror the `nested_context` truncation booleans for consumers that branch only on `statuses`; the full `nested_context` object remains a first-class field, never folded into `statuses`. Callers can ignore `statuses` safely.
400
+ `statuses` lists machine-readable collection and render conditions that previously appeared only inside `context` Markdown — plus the nested-context truncation flags, which exist only in machine form: skipped or absent sources, truncated sources, unreadable directory-tree entries, directory-tree entries whose directory holds more entries than one enumeration examines, sections omitted under the global output budget, truncated working-tree, declared-command or directory-tree listings, and truncated nested-`AGENTS.md` presence reports. A skipped or absent source reports the code of the condition the collector observed (`not_present`, `skipped_symlink`, `skipped_not_regular`, `skipped_encoding`, `skipped_unreadable`); the sentence in `context` is rendered from that same condition, so wording and code cannot drift apart. Each entry has stable `code`, `subject_kind`, and `subject` fields. The two `nested_agents_*` codes mirror the `nested_context` truncation booleans for consumers that branch only on `statuses`; the full `nested_context` object remains a first-class field, never folded into `statuses`. Callers can ignore `statuses` safely.
398
401
 
399
402
  The optional `selection` object is present only on a rendered `AGENTS.md` source. Its section entries
400
403
  contain heading, heading level, and fixed selection reasons; it never contains the original focus or
401
404
  target path. Existing per-source fields remain unchanged; the document's exact key
402
- set (now including `nested_context`) is named by its `schema_version`, described below.
405
+ set (which includes `nested_context`) is named by its `schema_version`, described below.
403
406
 
404
407
  `nested_context` is always present and existence-only: `files` lists repository-relative paths of
405
408
  non-directory `AGENTS.md` entries under subdirectories, found by a bounded scan that enumerates
@@ -408,11 +411,17 @@ no sizes, no mtimes and no contents. A symlinked (even dangling) `AGENTS.md` is
408
411
  existence comes from the directory entry itself; the scan never resolves what it points at. Any
409
412
  `.git`, `.venv`, `venv`, `node_modules` or `site-packages` directory is skipped at every depth:
410
413
  package-manager and interpreter-managed trees are not authored repository instructions.
411
- `list_truncated` means more matching entries exist beyond the report caps (32 paths and 4 KiB of
412
- path bytes); `scan_truncated` means the depth (4) or directory-count (2,000) budget was reached, or
413
- a directory or entry could not be read, so absence of a path is not proof of absence of the file.
414
- Paths are sanitized with the same escaping the Markdown uses for repository-derived text, so the
415
- document is always valid UTF-8. The corresponding
414
+ `list_truncated` means more matching entries exist beyond the report caps: 32 paths, and a 4 KiB
415
+ budget metered on the emitted array itself — each path's escaped, JSON-serialized string plus its
416
+ separator, with the array's brackets charged to the first entry — so the emitted array never costs
417
+ more than the budget it declares. Because quoting is counted, an array already near the cap can
418
+ report one path fewer than an older release did, and then says so through `list_truncated`.
419
+ `scan_truncated` means the depth (4) or directory-count (2,000) budget was reached, a directory
420
+ held more entries than one enumeration examines (1,024), or a directory or entry could not be
421
+ read, so absence of a path is not proof of absence of the file. Nested paths
422
+ are sanitized with the same escaping the Markdown uses for repository-derived text, so a nested name
423
+ the filesystem could not decode never breaks the document;
424
+ see “Limits” for the root paths that are emitted verbatim. The corresponding
416
425
  `nested_agents_list_truncated` and `nested_agents_scan_truncated` status entries mirror both flags.
417
426
  Presence is not instruction: whether a nested file applies, and its text, remain the caller's
418
427
  judgment; Markdown output is unchanged.
@@ -441,10 +450,10 @@ not otherwise change default Markdown bytes, exit codes or determinism.
441
450
 
442
451
  One historical exception is acknowledged: releases 1.0.0 and 1.1.0 both emitted `schema_version` 1
443
452
  although 1.1.0 added the top-level `statuses` key. From release 1.2.0 onward each emitted number
444
- names exactly one key set, and the `nested_context` renumber below enforces that rule going forward.
453
+ names exactly one key set, and a new number is issued whenever a key set would otherwise be reused.
445
454
 
446
455
  The JSON schema version and package version are independent: this build emits `schema_version` `3`
447
- for `--format json` and `4` for `--format json-compact`, while `tool.version` is `1.3.0`. Version 1
456
+ for `--format json` and `4` for `--format json-compact`, while `tool.version` is `1.3.2`. Version 1
448
457
  and 2 are the exact shapes already published in release 1.2.0 and are never reused: because
449
458
  `nested_context` changes the default document's key set, version 3 names the current full-document
450
459
  key set and version 4 names the compact projection.
@@ -497,18 +506,69 @@ scan that ends before EOF still reports the characters it could not select.
497
506
 
498
507
  ## Limits
499
508
 
500
- - Final stdout: 98,304 bytes
509
+ Final output bounds differ per format. The two stdout bounds below are enforced on the bytes written
510
+ to stdout and the nested report cap on the bytes the document emits for that array; every other
511
+ bullet is a per-component, read or capture bound, as each one states:
512
+
513
+ - Markdown stdout: 98,304 bytes. A section that cannot fit is replaced by an omission notice, and so
514
+ is every later section, so the limit holds without cutting a section mid-body.
515
+ - JSON and JSON-compact stdout: 8,388,608 bytes (8 MiB) for the serialized document, including its
516
+ trailing newline. This bound is fail-closed: if a repository's evidence cannot fit, the tool exits
517
+ 1, writes nothing to stdout and prints `error: JSON output exceeded the 8388608 byte document
518
+ limit`. It never emits a truncated document. The component budgets below keep legitimate
519
+ documents far inside it — the largest escape-dense repository audited for this release emitted
520
+ 263 KiB, of which 248 KiB was escaped `statuses` subjects — so it is a final backstop rather
521
+ than an expected truncation point.
522
+ - Component budgets below are metered on the Markdown `context` content, and that same string is the
523
+ JSON `context` value; because JSON escaping expands quotes, backslashes and control characters,
524
+ the serialized bytes can be several times larger. The JSON document also repeats the source
525
+ bodies, so no single component figure bounds JSON stdout.
501
526
  - `AGENTS.md`: 256-KiB bounded source scan; selected source plus selection audit remains at most
502
527
  16 KiB, including a 4-KiB maximum small head
503
528
  - `README.md`: 16 KiB
504
529
  - Each entry file: 8 KiB
505
530
  - All entry-file bodies: 24 KiB
506
531
  - Declared commands: 8 KiB
507
- - Directory tree: 12 KiB, 300 entries, and depth 2
508
- - Nested `AGENTS.md` presence scan: depth 4, 2,000 directories, and at most 32 reported paths;
532
+ - Directory tree: 12 KiB of listing body, 300 entries, and depth 2. Each directory is
533
+ enumerated up to 512 entries, keeping the alphabetically first names so the listing never
534
+ depends on operating system order. A directory that holds more is reported by a single
535
+ `Listing incomplete:` line stating the total, the limit and up to three names, and by at most
536
+ eight `directory_listing_incomplete` status entries naming individual directories plus one
537
+ aggregate entry giving the totals when more directories were capped than are named: beyond the
538
+ retained examples a capped directory is counted but not individually named, and no listing
539
+ claims completeness it does not have. That total covers the capped directories the listing
540
+ reached: a directory is reached only when its parent offered it inside the parent's own
541
+ alphabetical prefix and the entry budget kept it, so capped siblings displaced by the budget are
542
+ disclosed as their parent's truncation rather than as separate numbers.
543
+ Three limits act in sequence and a name survives only if all three leave room. The 512-entry
544
+ enumeration limit decides which names a directory offers at all; above it only an alphabetical
545
+ prefix is ever seen. The 300-entry item budget is then shared between a directory's
546
+ subdirectories and its non-directories: each may take up to half of what remains before the other
547
+ claims the surplus, which stops alphabetically-earlier directories from evicting every file, but
548
+ it is a share rather than a floor -- with a single slot left the non-directories take it. The 12
549
+ KiB listing budget then decides which kept names are rendered. Descent
550
+ happens after a directory's own entries, so deep content cannot displace top-level names, but
551
+ the tool promises no specific file: a root `README.md` is listed only when all three limits leave
552
+ room for it -- the enumeration offered it, the entry budget still has a slot, and the byte budget
553
+ still has room -- and each of those three can fail on its own. Being offered is not the same as
554
+ an uncapped root: the 512-entry cut drops only the names that sort after its retained prefix, so
555
+ an early-sorting `README.md` is still listed from a capped root while a late-sorting one never
556
+ reaches the listing. Whenever one of the three bites, the listing says so through the
557
+ note, the marker, or a `truncated` status instead of presenting itself as complete.
558
+ The `Listing incomplete:` line is charged to the 12 KiB listing budget before the body is cut,
559
+ so the claim can never inflate the section; its cost is paid in listing bytes, so a long note
560
+ removes that many bytes of entries, and the `truncated` status plus the marker inside the
561
+ fence say so. The heading and the code-fence lines are not metered, so the whole Markdown
562
+ section can exceed 12 KiB by that fixed few dozen bytes.
563
+ - Nested `AGENTS.md` presence scan: depth 4, 2,000 directories, at most 32 reported paths, a
564
+ 4 KiB budget metered on the emitted array, and at most 1,024 entries examined per directory;
509
565
  file contents are never read
510
566
  - Working-tree changes: 100 paths and 4 KiB
511
567
  - Recent commits: 8
568
+ - JSON-only listings have no byte budget of their own: `statuses` subjects are escaped names bounded
569
+ by the entry counts above, while `repository` and `sources[*].path` are emitted verbatim, so a
570
+ repository root the filesystem could not decode makes the machine formats exit 1 where Markdown
571
+ still renders
512
572
  - Git subprocess output: 16 MiB (bounded while reading)
513
573
  - Candidate file validation: 16 MiB hard read bound
514
574
 
@@ -545,7 +605,7 @@ or candidate-file content.
545
605
 
546
606
  ## Not Included
547
607
 
548
- Version 1.3.0 does not provide AI summaries, project-type detection, loading or transport of
608
+ Version 1.3.2 does not provide AI summaries, project-type detection, loading or transport of
549
609
  nested `AGENTS.md` contents (the JSON `nested_context` field reports bounded existence only),
550
610
  Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
551
611
  network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
@@ -583,6 +643,7 @@ PyPI file bytes, metadata and SHA-256, plus the publisher/tag/commit claims in P
583
643
  provenance. This is an identity check, not an independent cryptographic Sigstore verifier.
584
644
 
585
645
  - Tag/version or expected-commit mismatch and `just check` failure stop before build/upload.
646
+ - Annotated tag headers must match the exact tag name and commit, without extra or duplicate headers.
586
647
  - The build refuses to run unless `dist/` is empty, and refuses any produced file set other than
587
648
  the current version's wheel and sdist. A leftover artifact can carry a release filename while
588
649
  holding different bytes, so it is never treated as the current release.
@@ -8,14 +8,14 @@ Python standard library.
8
8
 
9
9
  ## Open-source quick start
10
10
 
11
- Current stable release: **1.3.0**. Local CLI for coding-agent workflows; renders deterministic Markdown or JSON.
11
+ Current stable release: **1.3.2**. Local CLI for coding-agent workflows; renders deterministic Markdown or JSON.
12
12
 
13
13
  ```bash
14
- uv tool install 'context-loader==1.3.0'
14
+ uv tool install 'context-loader==1.3.2'
15
15
  ```
16
16
 
17
17
  ```bash
18
- pip install 'context-loader==1.3.0'
18
+ pip install 'context-loader==1.3.2'
19
19
  ```
20
20
 
21
21
  ```bash
@@ -28,21 +28,21 @@ project-context --repo /path/to/repo --format json
28
28
 
29
29
  ## Install
30
30
 
31
- Source version: `1.3.0`. Install its matching published release or an exact source commit.
31
+ Source version: `1.3.2`. Install its matching published release or an exact source commit.
32
32
 
33
33
  Install via `uv`:
34
34
 
35
35
  ```bash
36
- uv tool install 'context-loader==1.3.0'
36
+ uv tool install 'context-loader==1.3.2'
37
37
  ```
38
38
 
39
39
  Install via `pip`:
40
40
 
41
41
  ```bash
42
- pip install 'context-loader==1.3.0'
42
+ pip install 'context-loader==1.3.2'
43
43
  ```
44
44
 
45
- For development or source-based installs tracking current repository (`1.3.0`):
45
+ For development or source-based installs tracking current repository (`1.3.2`):
46
46
 
47
47
  ```bash
48
48
  uv tool install git+https://github.com/xuanheng-tech/context-loader.git
@@ -108,6 +108,8 @@ and bare repositories are rejected in every mode.
108
108
  `--focus` and `--path` are optional, bounded selection signals for the root `AGENTS.md`. `--path`
109
109
  must be repository-relative. The collector does not retain either input in output or audit data.
110
110
  Calls that omit both options remain valid and use the conservative fallback described below.
111
+ Each value option (`--repo`, `--focus`, `--path`, `--format`) may be supplied once;
112
+ duplicates are rejected before collection rather than silently overriding an earlier value.
111
113
 
112
114
  ## Markdown Output
113
115
 
@@ -127,16 +129,17 @@ The output has no generated timestamp, AI summary, architecture inference, or di
127
129
  ## JSON Output
128
130
 
129
131
  `--format json` writes exactly one separator-compact UTF-8 JSON document plus one trailing newline
130
- to stdout. Keys are serialized in sorted order with `ensure_ascii=False`. `--format json-compact`
131
- uses the identical serialization; only the field set differs, as described under “Compact model
132
- consumption”. The declared contract for `--format json` is:
132
+ to stdout, within the JSON document bound under “Limits”. Keys are serialized in sorted order
133
+ with `ensure_ascii=False`. `--format json-compact` uses the identical serialization; only the field
134
+ set differs, as described under “Compact model consumption”. The declared contract for
135
+ `--format json` is:
133
136
 
134
137
  ```json
135
138
  {
136
139
  "schema_version": 3,
137
140
  "tool": {
138
141
  "name": "context-loader",
139
- "version": "1.3.0"
142
+ "version": "1.3.2"
140
143
  },
141
144
  "repository": {
142
145
  "requested_path": "/canonical/requested/path",
@@ -183,12 +186,12 @@ context, in assembly order, after the existing newline normalization and truncat
183
186
  distinguishes `repository` from `global`; version 1.0.0's fixed root-file selection currently emits
184
187
  only `repository` sources and does not add any global-file discovery.
185
188
 
186
- `statuses` lists machine-readable collection and render conditions that previously appeared only inside `context` Markdown — plus the nested-context truncation flags, which exist only in machine form: skipped or absent sources, truncated sources, unreadable directory-tree entries, sections omitted under the global output budget, truncated working-tree or declared-command listings, and truncated nested-`AGENTS.md` presence reports. Each entry has stable `code`, `subject_kind`, and `subject` fields. The two `nested_agents_*` codes mirror the `nested_context` truncation booleans for consumers that branch only on `statuses`; the full `nested_context` object remains a first-class field, never folded into `statuses`. Callers can ignore `statuses` safely.
189
+ `statuses` lists machine-readable collection and render conditions that previously appeared only inside `context` Markdown — plus the nested-context truncation flags, which exist only in machine form: skipped or absent sources, truncated sources, unreadable directory-tree entries, directory-tree entries whose directory holds more entries than one enumeration examines, sections omitted under the global output budget, truncated working-tree, declared-command or directory-tree listings, and truncated nested-`AGENTS.md` presence reports. A skipped or absent source reports the code of the condition the collector observed (`not_present`, `skipped_symlink`, `skipped_not_regular`, `skipped_encoding`, `skipped_unreadable`); the sentence in `context` is rendered from that same condition, so wording and code cannot drift apart. Each entry has stable `code`, `subject_kind`, and `subject` fields. The two `nested_agents_*` codes mirror the `nested_context` truncation booleans for consumers that branch only on `statuses`; the full `nested_context` object remains a first-class field, never folded into `statuses`. Callers can ignore `statuses` safely.
187
190
 
188
191
  The optional `selection` object is present only on a rendered `AGENTS.md` source. Its section entries
189
192
  contain heading, heading level, and fixed selection reasons; it never contains the original focus or
190
193
  target path. Existing per-source fields remain unchanged; the document's exact key
191
- set (now including `nested_context`) is named by its `schema_version`, described below.
194
+ set (which includes `nested_context`) is named by its `schema_version`, described below.
192
195
 
193
196
  `nested_context` is always present and existence-only: `files` lists repository-relative paths of
194
197
  non-directory `AGENTS.md` entries under subdirectories, found by a bounded scan that enumerates
@@ -197,11 +200,17 @@ no sizes, no mtimes and no contents. A symlinked (even dangling) `AGENTS.md` is
197
200
  existence comes from the directory entry itself; the scan never resolves what it points at. Any
198
201
  `.git`, `.venv`, `venv`, `node_modules` or `site-packages` directory is skipped at every depth:
199
202
  package-manager and interpreter-managed trees are not authored repository instructions.
200
- `list_truncated` means more matching entries exist beyond the report caps (32 paths and 4 KiB of
201
- path bytes); `scan_truncated` means the depth (4) or directory-count (2,000) budget was reached, or
202
- a directory or entry could not be read, so absence of a path is not proof of absence of the file.
203
- Paths are sanitized with the same escaping the Markdown uses for repository-derived text, so the
204
- document is always valid UTF-8. The corresponding
203
+ `list_truncated` means more matching entries exist beyond the report caps: 32 paths, and a 4 KiB
204
+ budget metered on the emitted array itself — each path's escaped, JSON-serialized string plus its
205
+ separator, with the array's brackets charged to the first entry — so the emitted array never costs
206
+ more than the budget it declares. Because quoting is counted, an array already near the cap can
207
+ report one path fewer than an older release did, and then says so through `list_truncated`.
208
+ `scan_truncated` means the depth (4) or directory-count (2,000) budget was reached, a directory
209
+ held more entries than one enumeration examines (1,024), or a directory or entry could not be
210
+ read, so absence of a path is not proof of absence of the file. Nested paths
211
+ are sanitized with the same escaping the Markdown uses for repository-derived text, so a nested name
212
+ the filesystem could not decode never breaks the document;
213
+ see “Limits” for the root paths that are emitted verbatim. The corresponding
205
214
  `nested_agents_list_truncated` and `nested_agents_scan_truncated` status entries mirror both flags.
206
215
  Presence is not instruction: whether a nested file applies, and its text, remain the caller's
207
216
  judgment; Markdown output is unchanged.
@@ -230,10 +239,10 @@ not otherwise change default Markdown bytes, exit codes or determinism.
230
239
 
231
240
  One historical exception is acknowledged: releases 1.0.0 and 1.1.0 both emitted `schema_version` 1
232
241
  although 1.1.0 added the top-level `statuses` key. From release 1.2.0 onward each emitted number
233
- names exactly one key set, and the `nested_context` renumber below enforces that rule going forward.
242
+ names exactly one key set, and a new number is issued whenever a key set would otherwise be reused.
234
243
 
235
244
  The JSON schema version and package version are independent: this build emits `schema_version` `3`
236
- for `--format json` and `4` for `--format json-compact`, while `tool.version` is `1.3.0`. Version 1
245
+ for `--format json` and `4` for `--format json-compact`, while `tool.version` is `1.3.2`. Version 1
237
246
  and 2 are the exact shapes already published in release 1.2.0 and are never reused: because
238
247
  `nested_context` changes the default document's key set, version 3 names the current full-document
239
248
  key set and version 4 names the compact projection.
@@ -286,18 +295,69 @@ scan that ends before EOF still reports the characters it could not select.
286
295
 
287
296
  ## Limits
288
297
 
289
- - Final stdout: 98,304 bytes
298
+ Final output bounds differ per format. The two stdout bounds below are enforced on the bytes written
299
+ to stdout and the nested report cap on the bytes the document emits for that array; every other
300
+ bullet is a per-component, read or capture bound, as each one states:
301
+
302
+ - Markdown stdout: 98,304 bytes. A section that cannot fit is replaced by an omission notice, and so
303
+ is every later section, so the limit holds without cutting a section mid-body.
304
+ - JSON and JSON-compact stdout: 8,388,608 bytes (8 MiB) for the serialized document, including its
305
+ trailing newline. This bound is fail-closed: if a repository's evidence cannot fit, the tool exits
306
+ 1, writes nothing to stdout and prints `error: JSON output exceeded the 8388608 byte document
307
+ limit`. It never emits a truncated document. The component budgets below keep legitimate
308
+ documents far inside it — the largest escape-dense repository audited for this release emitted
309
+ 263 KiB, of which 248 KiB was escaped `statuses` subjects — so it is a final backstop rather
310
+ than an expected truncation point.
311
+ - Component budgets below are metered on the Markdown `context` content, and that same string is the
312
+ JSON `context` value; because JSON escaping expands quotes, backslashes and control characters,
313
+ the serialized bytes can be several times larger. The JSON document also repeats the source
314
+ bodies, so no single component figure bounds JSON stdout.
290
315
  - `AGENTS.md`: 256-KiB bounded source scan; selected source plus selection audit remains at most
291
316
  16 KiB, including a 4-KiB maximum small head
292
317
  - `README.md`: 16 KiB
293
318
  - Each entry file: 8 KiB
294
319
  - All entry-file bodies: 24 KiB
295
320
  - Declared commands: 8 KiB
296
- - Directory tree: 12 KiB, 300 entries, and depth 2
297
- - Nested `AGENTS.md` presence scan: depth 4, 2,000 directories, and at most 32 reported paths;
321
+ - Directory tree: 12 KiB of listing body, 300 entries, and depth 2. Each directory is
322
+ enumerated up to 512 entries, keeping the alphabetically first names so the listing never
323
+ depends on operating system order. A directory that holds more is reported by a single
324
+ `Listing incomplete:` line stating the total, the limit and up to three names, and by at most
325
+ eight `directory_listing_incomplete` status entries naming individual directories plus one
326
+ aggregate entry giving the totals when more directories were capped than are named: beyond the
327
+ retained examples a capped directory is counted but not individually named, and no listing
328
+ claims completeness it does not have. That total covers the capped directories the listing
329
+ reached: a directory is reached only when its parent offered it inside the parent's own
330
+ alphabetical prefix and the entry budget kept it, so capped siblings displaced by the budget are
331
+ disclosed as their parent's truncation rather than as separate numbers.
332
+ Three limits act in sequence and a name survives only if all three leave room. The 512-entry
333
+ enumeration limit decides which names a directory offers at all; above it only an alphabetical
334
+ prefix is ever seen. The 300-entry item budget is then shared between a directory's
335
+ subdirectories and its non-directories: each may take up to half of what remains before the other
336
+ claims the surplus, which stops alphabetically-earlier directories from evicting every file, but
337
+ it is a share rather than a floor -- with a single slot left the non-directories take it. The 12
338
+ KiB listing budget then decides which kept names are rendered. Descent
339
+ happens after a directory's own entries, so deep content cannot displace top-level names, but
340
+ the tool promises no specific file: a root `README.md` is listed only when all three limits leave
341
+ room for it -- the enumeration offered it, the entry budget still has a slot, and the byte budget
342
+ still has room -- and each of those three can fail on its own. Being offered is not the same as
343
+ an uncapped root: the 512-entry cut drops only the names that sort after its retained prefix, so
344
+ an early-sorting `README.md` is still listed from a capped root while a late-sorting one never
345
+ reaches the listing. Whenever one of the three bites, the listing says so through the
346
+ note, the marker, or a `truncated` status instead of presenting itself as complete.
347
+ The `Listing incomplete:` line is charged to the 12 KiB listing budget before the body is cut,
348
+ so the claim can never inflate the section; its cost is paid in listing bytes, so a long note
349
+ removes that many bytes of entries, and the `truncated` status plus the marker inside the
350
+ fence say so. The heading and the code-fence lines are not metered, so the whole Markdown
351
+ section can exceed 12 KiB by that fixed few dozen bytes.
352
+ - Nested `AGENTS.md` presence scan: depth 4, 2,000 directories, at most 32 reported paths, a
353
+ 4 KiB budget metered on the emitted array, and at most 1,024 entries examined per directory;
298
354
  file contents are never read
299
355
  - Working-tree changes: 100 paths and 4 KiB
300
356
  - Recent commits: 8
357
+ - JSON-only listings have no byte budget of their own: `statuses` subjects are escaped names bounded
358
+ by the entry counts above, while `repository` and `sources[*].path` are emitted verbatim, so a
359
+ repository root the filesystem could not decode makes the machine formats exit 1 where Markdown
360
+ still renders
301
361
  - Git subprocess output: 16 MiB (bounded while reading)
302
362
  - Candidate file validation: 16 MiB hard read bound
303
363
 
@@ -334,7 +394,7 @@ or candidate-file content.
334
394
 
335
395
  ## Not Included
336
396
 
337
- Version 1.3.0 does not provide AI summaries, project-type detection, loading or transport of
397
+ Version 1.3.2 does not provide AI summaries, project-type detection, loading or transport of
338
398
  nested `AGENTS.md` contents (the JSON `nested_context` field reports bounded existence only),
339
399
  Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
340
400
  network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
@@ -372,6 +432,7 @@ PyPI file bytes, metadata and SHA-256, plus the publisher/tag/commit claims in P
372
432
  provenance. This is an identity check, not an independent cryptographic Sigstore verifier.
373
433
 
374
434
  - Tag/version or expected-commit mismatch and `just check` failure stop before build/upload.
435
+ - Annotated tag headers must match the exact tag name and commit, without extra or duplicate headers.
375
436
  - The build refuses to run unless `dist/` is empty, and refuses any produced file set other than
376
437
  the current version's wheel and sdist. A leftover artifact can carry a release filename while
377
438
  holding different bytes, so it is never treated as the current release.
@@ -1,3 +1,3 @@
1
1
  """Project Context Loader."""
2
2
 
3
- __version__ = "1.3.0"
3
+ __version__ = "1.3.2"