context-loader 1.3.1__tar.gz → 1.3.3__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.
- {context_loader-1.3.1 → context_loader-1.3.3}/PKG-INFO +105 -20
- {context_loader-1.3.1 → context_loader-1.3.3}/README.md +104 -19
- {context_loader-1.3.1 → context_loader-1.3.3}/context_loader/__init__.py +1 -1
- {context_loader-1.3.1 → context_loader-1.3.3}/context_loader/application.py +74 -42
- {context_loader-1.3.1 → context_loader-1.3.3}/context_loader/cli.py +21 -1
- {context_loader-1.3.1 → context_loader-1.3.3}/context_loader/collect.py +335 -363
- context_loader-1.3.3/context_loader/filesystem.py +248 -0
- {context_loader-1.3.1 → context_loader-1.3.3}/context_loader/git.py +62 -0
- context_loader-1.3.3/context_loader/model.py +186 -0
- {context_loader-1.3.1 → context_loader-1.3.3}/context_loader/render.py +181 -64
- {context_loader-1.3.1 → context_loader-1.3.3}/pyproject.toml +1 -1
- {context_loader-1.3.1 → context_loader-1.3.3}/pyproject.toml.orig +1 -1
- {context_loader-1.3.1 → context_loader-1.3.3}/tool_cli_contract.json +1 -1
- {context_loader-1.3.1 → context_loader-1.3.3}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: context-loader
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
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.
|
|
222
|
+
Current stable release: **1.3.3**. Local CLI for coding-agent workflows; renders deterministic Markdown or JSON.
|
|
223
223
|
|
|
224
224
|
```bash
|
|
225
|
-
uv tool install 'context-loader==1.3.
|
|
225
|
+
uv tool install 'context-loader==1.3.3'
|
|
226
226
|
```
|
|
227
227
|
|
|
228
228
|
```bash
|
|
229
|
-
pip install 'context-loader==1.3.
|
|
229
|
+
pip install 'context-loader==1.3.3'
|
|
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.
|
|
242
|
+
Source version: `1.3.3`. 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.
|
|
247
|
+
uv tool install 'context-loader==1.3.3'
|
|
248
248
|
```
|
|
249
249
|
|
|
250
250
|
Install via `pip`:
|
|
251
251
|
|
|
252
252
|
```bash
|
|
253
|
-
pip install 'context-loader==1.3.
|
|
253
|
+
pip install 'context-loader==1.3.3'
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
-
For development or source-based installs tracking current repository (`1.3.
|
|
256
|
+
For development or source-based installs tracking current repository (`1.3.3`):
|
|
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
|
|
|
@@ -348,7 +350,7 @@ set differs, as described under “Compact model consumption”. The declared co
|
|
|
348
350
|
"schema_version": 3,
|
|
349
351
|
"tool": {
|
|
350
352
|
"name": "context-loader",
|
|
351
|
-
"version": "1.3.
|
|
353
|
+
"version": "1.3.3"
|
|
352
354
|
},
|
|
353
355
|
"repository": {
|
|
354
356
|
"requested_path": "/canonical/requested/path",
|
|
@@ -395,12 +397,19 @@ context, in assembly order, after the existing newline normalization and truncat
|
|
|
395
397
|
distinguishes `repository` from `global`; version 1.0.0's fixed root-file selection currently emits
|
|
396
398
|
only `repository` sources and does not add any global-file discovery.
|
|
397
399
|
|
|
398
|
-
`statuses` lists
|
|
400
|
+
`statuses` lists skipped or absent sources, truncated sources, unreadable tree entries,
|
|
401
|
+
directories whose listing exceeds its retention cap, globally omitted sections, and truncated
|
|
402
|
+
working-tree, command, tree or nested-`AGENTS.md` listings. A skipped or absent source reports
|
|
403
|
+
the observed condition (`not_present`, `skipped_symlink`, `skipped_not_regular`,
|
|
404
|
+
`skipped_encoding`, `skipped_unreadable`); its sentence in `context` comes from that same
|
|
405
|
+
condition. Each entry has stable `code`, `subject_kind`, and `subject` fields. The two
|
|
406
|
+
`nested_agents_*` codes mirror the `nested_context` truncation booleans; the complete object
|
|
407
|
+
remains a separate field. Callers can ignore `statuses` safely.
|
|
399
408
|
|
|
400
409
|
The optional `selection` object is present only on a rendered `AGENTS.md` source. Its section entries
|
|
401
410
|
contain heading, heading level, and fixed selection reasons; it never contains the original focus or
|
|
402
411
|
target path. Existing per-source fields remain unchanged; the document's exact key
|
|
403
|
-
set (
|
|
412
|
+
set (which includes `nested_context`) is named by its `schema_version`, described below.
|
|
404
413
|
|
|
405
414
|
`nested_context` is always present and existence-only: `files` lists repository-relative paths of
|
|
406
415
|
non-directory `AGENTS.md` entries under subdirectories, found by a bounded scan that enumerates
|
|
@@ -414,8 +423,9 @@ budget metered on the emitted array itself — each path's escaped, JSON-seriali
|
|
|
414
423
|
separator, with the array's brackets charged to the first entry — so the emitted array never costs
|
|
415
424
|
more than the budget it declares. Because quoting is counted, an array already near the cap can
|
|
416
425
|
report one path fewer than an older release did, and then says so through `list_truncated`.
|
|
417
|
-
`scan_truncated` means the depth (4) or directory-count (2,000) budget was reached,
|
|
418
|
-
|
|
426
|
+
`scan_truncated` means the depth (4) or directory-count (2,000) budget was reached, a directory
|
|
427
|
+
held more entries than one listing retains (1,024), or a directory or entry could not be
|
|
428
|
+
read, so absence of a path is not proof of absence of the file. Nested paths
|
|
419
429
|
are sanitized with the same escaping the Markdown uses for repository-derived text, so a nested name
|
|
420
430
|
the filesystem could not decode never breaks the document;
|
|
421
431
|
see “Limits” for the root paths that are emitted verbatim. The corresponding
|
|
@@ -423,6 +433,16 @@ see “Limits” for the root paths that are emitted verbatim. The corresponding
|
|
|
423
433
|
Presence is not instruction: whether a nested file applies, and its text, remain the caller's
|
|
424
434
|
judgment; Markdown output is unchanged.
|
|
425
435
|
|
|
436
|
+
Escaped names remain reversible and distinct: a literal backslash becomes `\\`, a backtick
|
|
437
|
+
becomes `\x60`, ASCII controls and undecodable filesystem bytes become `\xNN`, and other
|
|
438
|
+
non-printable Unicode characters become `\uNNNN` or `\UNNNNNNNN`. Printable Unicode stays
|
|
439
|
+
unchanged. After JSON decoding, recover POSIX path bytes by reading escapes from left to right:
|
|
440
|
+
`\\` yields one backslash byte, `\xNN` yields that byte, and Unicode escapes and ordinary
|
|
441
|
+
characters yield their UTF-8 bytes. Thus a backtick, a literal `\x60`, an undecodable byte
|
|
442
|
+
`0x80`, and Unicode `U+0080` all have different representations. This applies to nested paths
|
|
443
|
+
and displayed tree/change/status names; the verbatim root/source paths retain their existing
|
|
444
|
+
semantics.
|
|
445
|
+
|
|
426
446
|
### Compact model consumption (`--format json-compact`)
|
|
427
447
|
|
|
428
448
|
`--format json-compact` emits a `schema_version` 4 document: exactly the version-3 document with
|
|
@@ -447,10 +467,10 @@ not otherwise change default Markdown bytes, exit codes or determinism.
|
|
|
447
467
|
|
|
448
468
|
One historical exception is acknowledged: releases 1.0.0 and 1.1.0 both emitted `schema_version` 1
|
|
449
469
|
although 1.1.0 added the top-level `statuses` key. From release 1.2.0 onward each emitted number
|
|
450
|
-
names exactly one key set, and
|
|
470
|
+
names exactly one key set, and a new number is issued whenever a key set would otherwise be reused.
|
|
451
471
|
|
|
452
472
|
The JSON schema version and package version are independent: this build emits `schema_version` `3`
|
|
453
|
-
for `--format json` and `4` for `--format json-compact`, while `tool.version` is `1.3.
|
|
473
|
+
for `--format json` and `4` for `--format json-compact`, while `tool.version` is `1.3.3`. Version 1
|
|
454
474
|
and 2 are the exact shapes already published in release 1.2.0 and are never reused: because
|
|
455
475
|
`nested_context` changes the default document's key set, version 3 names the current full-document
|
|
456
476
|
key set and version 4 names the compact projection.
|
|
@@ -524,11 +544,51 @@ bullet is a per-component, read or capture bound, as each one states:
|
|
|
524
544
|
16 KiB, including a 4-KiB maximum small head
|
|
525
545
|
- `README.md`: 16 KiB
|
|
526
546
|
- Each entry file: 8 KiB
|
|
527
|
-
- All entry-file bodies: 24 KiB
|
|
547
|
+
- All entry-file bodies: 24 KiB, including truncation markers. Room is reserved for markers
|
|
548
|
+
on later text sources, so exhausting the budget never hides that their bodies were cut.
|
|
528
549
|
- Declared commands: 8 KiB
|
|
529
|
-
- Directory tree: 12 KiB, 300 entries, and depth 2
|
|
530
|
-
|
|
531
|
-
|
|
550
|
+
- Directory tree: 12 KiB of listing body, 300 entries, and depth 2. Each directory is
|
|
551
|
+
retains and classifies up to 512 entries, keeping the alphabetically first names so the listing never
|
|
552
|
+
depends on operating system order. A directory that holds more is reported by a single
|
|
553
|
+
`Listing incomplete:` line stating the total, the limit and up to three names, and by at most
|
|
554
|
+
eight `directory_listing_incomplete` status entries naming individual directories plus one
|
|
555
|
+
aggregate entry giving the totals when more directories were capped than are named: beyond the
|
|
556
|
+
retained examples a capped directory is counted but not individually named, and no listing
|
|
557
|
+
claims completeness it does not have. That total covers the capped directories the listing
|
|
558
|
+
reached: a directory is reached only when its parent offered it inside the parent's own
|
|
559
|
+
alphabetical prefix and the entry budget kept it, so capped siblings displaced by the budget are
|
|
560
|
+
disclosed as their parent's truncation rather than as separate numbers.
|
|
561
|
+
Three limits act in sequence and a name survives only if all three leave room. The 512-entry
|
|
562
|
+
retention limit decides which names a directory offers at all; above it only an alphabetical
|
|
563
|
+
prefix is classified. All names must still be scanned to find that prefix, subject to the
|
|
564
|
+
separate hard name-scan bound below; the retention cap bounds memory and classification work.
|
|
565
|
+
The 300-entry item budget is then shared between a directory's
|
|
566
|
+
subdirectories and its non-directories: each may take up to half of what remains before the other
|
|
567
|
+
claims the surplus, which stops alphabetically-earlier directories from evicting every file, but
|
|
568
|
+
it is a share rather than a floor -- with a single slot left the non-directories take it. The 12
|
|
569
|
+
KiB listing budget then decides which kept names are rendered. Descent
|
|
570
|
+
happens after a directory's own entries, so deep content cannot displace top-level names, but
|
|
571
|
+
the tool promises no specific file: a root `README.md` is listed only when all three limits leave
|
|
572
|
+
room for it -- the enumeration offered it, the entry budget still has a slot, and the byte budget
|
|
573
|
+
still has room -- and each of those three can fail on its own. Being offered is not the same as
|
|
574
|
+
an uncapped root: the 512-entry cut drops only the names that sort after its retained prefix, so
|
|
575
|
+
an early-sorting `README.md` is still listed from a capped root while a late-sorting one never
|
|
576
|
+
reaches the listing. Whenever one of the three bites, the listing says so through the
|
|
577
|
+
note, the marker, or a `truncated` status instead of presenting itself as complete.
|
|
578
|
+
The `Listing incomplete:` line is charged to the 12 KiB listing budget before the body is cut,
|
|
579
|
+
so the claim can never inflate the section; its cost is paid in listing bytes, so a long note
|
|
580
|
+
removes that many bytes of entries, and the `truncated` status plus the marker inside the
|
|
581
|
+
fence say so. The heading and the code-fence lines are not metered, so the whole Markdown
|
|
582
|
+
section can exceed 12 KiB by that fixed few dozen bytes.
|
|
583
|
+
- Nested `AGENTS.md` presence scan: depth 4, 2,000 directories, at most 32 reported paths, a
|
|
584
|
+
4 KiB budget metered on the emitted array, and at most 1,024 entries retained and classified
|
|
585
|
+
per directory. Names are scanned to select that prefix, subject to the separate hard
|
|
586
|
+
name-scan bound below; file contents are never read
|
|
587
|
+
- Directory name scans stop on the first entry beyond 65,536 names per directory. An
|
|
588
|
+
over-limit directory's entire partial prefix is discarded to keep results independent of
|
|
589
|
+
operating system order. The tree emits `Skipped: directory name scan limit reached.` and
|
|
590
|
+
a `directory_scan_limit_exceeded` status; the nested presence scan sets `scan_truncated`.
|
|
591
|
+
This bounds name iteration, not elapsed time on a blocked filesystem call.
|
|
532
592
|
- Working-tree changes: 100 paths and 4 KiB
|
|
533
593
|
- Recent commits: 8
|
|
534
594
|
- JSON-only listings have no byte budget of their own: `statuses` subjects are escaped names bounded
|
|
@@ -536,6 +596,7 @@ bullet is a per-component, read or capture bound, as each one states:
|
|
|
536
596
|
repository root the filesystem could not decode makes the machine formats exit 1 where Markdown
|
|
537
597
|
still renders
|
|
538
598
|
- Git subprocess output: 16 MiB (bounded while reading)
|
|
599
|
+
- Git status preflight: at most 128 repositories, including initialized nested submodules
|
|
539
600
|
- Candidate file validation: 16 MiB hard read bound
|
|
540
601
|
|
|
541
602
|
Truncation occurs only at complete UTF-8 and line boundaries and is marked explicitly.
|
|
@@ -559,6 +620,13 @@ the remote.
|
|
|
559
620
|
this is normal after cloning an empty remote.
|
|
560
621
|
- `Ahead / behind: not available` means the commit relationship cannot currently be computed.
|
|
561
622
|
Counts are reported only when the configured upstream commit is available locally.
|
|
623
|
+
- Executable Git clean/process filters can run during `status`, even with optional locks
|
|
624
|
+
disabled. Before reading status, the loader checks effective repository configuration,
|
|
625
|
+
including includes, worktree configuration and initialized nested submodules. A non-empty
|
|
626
|
+
clean/process command causes a safe error with no stdout, even if no current attribute uses
|
|
627
|
+
it. Disabling a driver silently would change cleanliness semantics. Filter-dependent repositories
|
|
628
|
+
(including locally configured Git LFS drivers) therefore need ordinary Git inspection outside
|
|
629
|
+
this loader. Git protocols are also disabled to prevent lazy object retrieval.
|
|
562
630
|
|
|
563
631
|
## Exit Codes
|
|
564
632
|
|
|
@@ -571,7 +639,7 @@ or candidate-file content.
|
|
|
571
639
|
|
|
572
640
|
## Not Included
|
|
573
641
|
|
|
574
|
-
Version 1.3.
|
|
642
|
+
Version 1.3.3 does not provide AI summaries, project-type detection, loading or transport of
|
|
575
643
|
nested `AGENTS.md` contents (the JSON `nested_context` field reports bounded existence only),
|
|
576
644
|
Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
|
|
577
645
|
network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
|
|
@@ -584,6 +652,17 @@ Run the complete local check:
|
|
|
584
652
|
just check
|
|
585
653
|
```
|
|
586
654
|
|
|
655
|
+
The lockfile uses public PyPI artifact URLs; no organization mirror or local Renovate preset
|
|
656
|
+
is needed. Renovate keeps scheduled, rate-limited updates and manual merges using a public
|
|
657
|
+
recommended preset plus the repository's explicit settings. Python 3.12 remains the tested
|
|
658
|
+
runtime; widening that range requires testing those interpreters first.
|
|
659
|
+
|
|
660
|
+
Git test fixtures discard inherited `GIT_*` routing/configuration before creating repositories.
|
|
661
|
+
Permission-sensitive tests must run as a non-root user. Gitea uses `just ci-check`: non-root
|
|
662
|
+
runners execute directly; root containers use `runuser` with an existing account named by
|
|
663
|
+
`CONTEXT_LOADER_CI_USER` (default `ubuntu`). That account must be able to execute the installed
|
|
664
|
+
`uv`, Python and `just`. No `sudo` dependency is required for this CI step.
|
|
665
|
+
|
|
587
666
|
## Version maintenance
|
|
588
667
|
|
|
589
668
|
The versions in `pyproject.toml` and `context_loader/__init__.py`, the `tool_version` in
|
|
@@ -604,11 +683,17 @@ only verifies the public release, mirrors its exact formal tag object if missing
|
|
|
604
683
|
the Gitea Release record. It builds no package and has no PyPI publishing credentials.
|
|
605
684
|
No private endpoint or credential is needed on GitHub.
|
|
606
685
|
|
|
686
|
+
For environments that permit direct public reads but share an exhausted proxy quota,
|
|
687
|
+
set the Gitea Actions repository variable `RELEASE_PUBLIC_API_DIRECT=true`. The record
|
|
688
|
+
step appends only public GitHub API/PyPI hosts to the existing proxy bypass lists. Without
|
|
689
|
+
this explicit option, the workflow uses its inherited proxy settings.
|
|
690
|
+
|
|
607
691
|
Release checks use Python 3.12, Git, and the runner's existing OpenSSL CLI. They compare downloaded
|
|
608
692
|
PyPI file bytes, metadata and SHA-256, plus the publisher/tag/commit claims in PyPI's HTTPS-served
|
|
609
693
|
provenance. This is an identity check, not an independent cryptographic Sigstore verifier.
|
|
610
694
|
|
|
611
695
|
- Tag/version or expected-commit mismatch and `just check` failure stop before build/upload.
|
|
696
|
+
- Annotated tag headers must match the exact tag name and commit, without extra or duplicate headers.
|
|
612
697
|
- The build refuses to run unless `dist/` is empty, and refuses any produced file set other than
|
|
613
698
|
the current version's wheel and sdist. A leftover artifact can carry a release filename while
|
|
614
699
|
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.
|
|
11
|
+
Current stable release: **1.3.3**. Local CLI for coding-agent workflows; renders deterministic Markdown or JSON.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
uv tool install 'context-loader==1.3.
|
|
14
|
+
uv tool install 'context-loader==1.3.3'
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
pip install 'context-loader==1.3.
|
|
18
|
+
pip install 'context-loader==1.3.3'
|
|
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.
|
|
31
|
+
Source version: `1.3.3`. 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.
|
|
36
|
+
uv tool install 'context-loader==1.3.3'
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Install via `pip`:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
pip install 'context-loader==1.3.
|
|
42
|
+
pip install 'context-loader==1.3.3'
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
For development or source-based installs tracking current repository (`1.3.
|
|
45
|
+
For development or source-based installs tracking current repository (`1.3.3`):
|
|
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
|
|
|
@@ -137,7 +139,7 @@ set differs, as described under “Compact model consumption”. The declared co
|
|
|
137
139
|
"schema_version": 3,
|
|
138
140
|
"tool": {
|
|
139
141
|
"name": "context-loader",
|
|
140
|
-
"version": "1.3.
|
|
142
|
+
"version": "1.3.3"
|
|
141
143
|
},
|
|
142
144
|
"repository": {
|
|
143
145
|
"requested_path": "/canonical/requested/path",
|
|
@@ -184,12 +186,19 @@ context, in assembly order, after the existing newline normalization and truncat
|
|
|
184
186
|
distinguishes `repository` from `global`; version 1.0.0's fixed root-file selection currently emits
|
|
185
187
|
only `repository` sources and does not add any global-file discovery.
|
|
186
188
|
|
|
187
|
-
`statuses` lists
|
|
189
|
+
`statuses` lists skipped or absent sources, truncated sources, unreadable tree entries,
|
|
190
|
+
directories whose listing exceeds its retention cap, globally omitted sections, and truncated
|
|
191
|
+
working-tree, command, tree or nested-`AGENTS.md` listings. A skipped or absent source reports
|
|
192
|
+
the observed condition (`not_present`, `skipped_symlink`, `skipped_not_regular`,
|
|
193
|
+
`skipped_encoding`, `skipped_unreadable`); its sentence in `context` comes from that same
|
|
194
|
+
condition. Each entry has stable `code`, `subject_kind`, and `subject` fields. The two
|
|
195
|
+
`nested_agents_*` codes mirror the `nested_context` truncation booleans; the complete object
|
|
196
|
+
remains a separate field. Callers can ignore `statuses` safely.
|
|
188
197
|
|
|
189
198
|
The optional `selection` object is present only on a rendered `AGENTS.md` source. Its section entries
|
|
190
199
|
contain heading, heading level, and fixed selection reasons; it never contains the original focus or
|
|
191
200
|
target path. Existing per-source fields remain unchanged; the document's exact key
|
|
192
|
-
set (
|
|
201
|
+
set (which includes `nested_context`) is named by its `schema_version`, described below.
|
|
193
202
|
|
|
194
203
|
`nested_context` is always present and existence-only: `files` lists repository-relative paths of
|
|
195
204
|
non-directory `AGENTS.md` entries under subdirectories, found by a bounded scan that enumerates
|
|
@@ -203,8 +212,9 @@ budget metered on the emitted array itself — each path's escaped, JSON-seriali
|
|
|
203
212
|
separator, with the array's brackets charged to the first entry — so the emitted array never costs
|
|
204
213
|
more than the budget it declares. Because quoting is counted, an array already near the cap can
|
|
205
214
|
report one path fewer than an older release did, and then says so through `list_truncated`.
|
|
206
|
-
`scan_truncated` means the depth (4) or directory-count (2,000) budget was reached,
|
|
207
|
-
|
|
215
|
+
`scan_truncated` means the depth (4) or directory-count (2,000) budget was reached, a directory
|
|
216
|
+
held more entries than one listing retains (1,024), or a directory or entry could not be
|
|
217
|
+
read, so absence of a path is not proof of absence of the file. Nested paths
|
|
208
218
|
are sanitized with the same escaping the Markdown uses for repository-derived text, so a nested name
|
|
209
219
|
the filesystem could not decode never breaks the document;
|
|
210
220
|
see “Limits” for the root paths that are emitted verbatim. The corresponding
|
|
@@ -212,6 +222,16 @@ see “Limits” for the root paths that are emitted verbatim. The corresponding
|
|
|
212
222
|
Presence is not instruction: whether a nested file applies, and its text, remain the caller's
|
|
213
223
|
judgment; Markdown output is unchanged.
|
|
214
224
|
|
|
225
|
+
Escaped names remain reversible and distinct: a literal backslash becomes `\\`, a backtick
|
|
226
|
+
becomes `\x60`, ASCII controls and undecodable filesystem bytes become `\xNN`, and other
|
|
227
|
+
non-printable Unicode characters become `\uNNNN` or `\UNNNNNNNN`. Printable Unicode stays
|
|
228
|
+
unchanged. After JSON decoding, recover POSIX path bytes by reading escapes from left to right:
|
|
229
|
+
`\\` yields one backslash byte, `\xNN` yields that byte, and Unicode escapes and ordinary
|
|
230
|
+
characters yield their UTF-8 bytes. Thus a backtick, a literal `\x60`, an undecodable byte
|
|
231
|
+
`0x80`, and Unicode `U+0080` all have different representations. This applies to nested paths
|
|
232
|
+
and displayed tree/change/status names; the verbatim root/source paths retain their existing
|
|
233
|
+
semantics.
|
|
234
|
+
|
|
215
235
|
### Compact model consumption (`--format json-compact`)
|
|
216
236
|
|
|
217
237
|
`--format json-compact` emits a `schema_version` 4 document: exactly the version-3 document with
|
|
@@ -236,10 +256,10 @@ not otherwise change default Markdown bytes, exit codes or determinism.
|
|
|
236
256
|
|
|
237
257
|
One historical exception is acknowledged: releases 1.0.0 and 1.1.0 both emitted `schema_version` 1
|
|
238
258
|
although 1.1.0 added the top-level `statuses` key. From release 1.2.0 onward each emitted number
|
|
239
|
-
names exactly one key set, and
|
|
259
|
+
names exactly one key set, and a new number is issued whenever a key set would otherwise be reused.
|
|
240
260
|
|
|
241
261
|
The JSON schema version and package version are independent: this build emits `schema_version` `3`
|
|
242
|
-
for `--format json` and `4` for `--format json-compact`, while `tool.version` is `1.3.
|
|
262
|
+
for `--format json` and `4` for `--format json-compact`, while `tool.version` is `1.3.3`. Version 1
|
|
243
263
|
and 2 are the exact shapes already published in release 1.2.0 and are never reused: because
|
|
244
264
|
`nested_context` changes the default document's key set, version 3 names the current full-document
|
|
245
265
|
key set and version 4 names the compact projection.
|
|
@@ -313,11 +333,51 @@ bullet is a per-component, read or capture bound, as each one states:
|
|
|
313
333
|
16 KiB, including a 4-KiB maximum small head
|
|
314
334
|
- `README.md`: 16 KiB
|
|
315
335
|
- Each entry file: 8 KiB
|
|
316
|
-
- All entry-file bodies: 24 KiB
|
|
336
|
+
- All entry-file bodies: 24 KiB, including truncation markers. Room is reserved for markers
|
|
337
|
+
on later text sources, so exhausting the budget never hides that their bodies were cut.
|
|
317
338
|
- Declared commands: 8 KiB
|
|
318
|
-
- Directory tree: 12 KiB, 300 entries, and depth 2
|
|
319
|
-
|
|
320
|
-
|
|
339
|
+
- Directory tree: 12 KiB of listing body, 300 entries, and depth 2. Each directory is
|
|
340
|
+
retains and classifies up to 512 entries, keeping the alphabetically first names so the listing never
|
|
341
|
+
depends on operating system order. A directory that holds more is reported by a single
|
|
342
|
+
`Listing incomplete:` line stating the total, the limit and up to three names, and by at most
|
|
343
|
+
eight `directory_listing_incomplete` status entries naming individual directories plus one
|
|
344
|
+
aggregate entry giving the totals when more directories were capped than are named: beyond the
|
|
345
|
+
retained examples a capped directory is counted but not individually named, and no listing
|
|
346
|
+
claims completeness it does not have. That total covers the capped directories the listing
|
|
347
|
+
reached: a directory is reached only when its parent offered it inside the parent's own
|
|
348
|
+
alphabetical prefix and the entry budget kept it, so capped siblings displaced by the budget are
|
|
349
|
+
disclosed as their parent's truncation rather than as separate numbers.
|
|
350
|
+
Three limits act in sequence and a name survives only if all three leave room. The 512-entry
|
|
351
|
+
retention limit decides which names a directory offers at all; above it only an alphabetical
|
|
352
|
+
prefix is classified. All names must still be scanned to find that prefix, subject to the
|
|
353
|
+
separate hard name-scan bound below; the retention cap bounds memory and classification work.
|
|
354
|
+
The 300-entry item budget is then shared between a directory's
|
|
355
|
+
subdirectories and its non-directories: each may take up to half of what remains before the other
|
|
356
|
+
claims the surplus, which stops alphabetically-earlier directories from evicting every file, but
|
|
357
|
+
it is a share rather than a floor -- with a single slot left the non-directories take it. The 12
|
|
358
|
+
KiB listing budget then decides which kept names are rendered. Descent
|
|
359
|
+
happens after a directory's own entries, so deep content cannot displace top-level names, but
|
|
360
|
+
the tool promises no specific file: a root `README.md` is listed only when all three limits leave
|
|
361
|
+
room for it -- the enumeration offered it, the entry budget still has a slot, and the byte budget
|
|
362
|
+
still has room -- and each of those three can fail on its own. Being offered is not the same as
|
|
363
|
+
an uncapped root: the 512-entry cut drops only the names that sort after its retained prefix, so
|
|
364
|
+
an early-sorting `README.md` is still listed from a capped root while a late-sorting one never
|
|
365
|
+
reaches the listing. Whenever one of the three bites, the listing says so through the
|
|
366
|
+
note, the marker, or a `truncated` status instead of presenting itself as complete.
|
|
367
|
+
The `Listing incomplete:` line is charged to the 12 KiB listing budget before the body is cut,
|
|
368
|
+
so the claim can never inflate the section; its cost is paid in listing bytes, so a long note
|
|
369
|
+
removes that many bytes of entries, and the `truncated` status plus the marker inside the
|
|
370
|
+
fence say so. The heading and the code-fence lines are not metered, so the whole Markdown
|
|
371
|
+
section can exceed 12 KiB by that fixed few dozen bytes.
|
|
372
|
+
- Nested `AGENTS.md` presence scan: depth 4, 2,000 directories, at most 32 reported paths, a
|
|
373
|
+
4 KiB budget metered on the emitted array, and at most 1,024 entries retained and classified
|
|
374
|
+
per directory. Names are scanned to select that prefix, subject to the separate hard
|
|
375
|
+
name-scan bound below; file contents are never read
|
|
376
|
+
- Directory name scans stop on the first entry beyond 65,536 names per directory. An
|
|
377
|
+
over-limit directory's entire partial prefix is discarded to keep results independent of
|
|
378
|
+
operating system order. The tree emits `Skipped: directory name scan limit reached.` and
|
|
379
|
+
a `directory_scan_limit_exceeded` status; the nested presence scan sets `scan_truncated`.
|
|
380
|
+
This bounds name iteration, not elapsed time on a blocked filesystem call.
|
|
321
381
|
- Working-tree changes: 100 paths and 4 KiB
|
|
322
382
|
- Recent commits: 8
|
|
323
383
|
- JSON-only listings have no byte budget of their own: `statuses` subjects are escaped names bounded
|
|
@@ -325,6 +385,7 @@ bullet is a per-component, read or capture bound, as each one states:
|
|
|
325
385
|
repository root the filesystem could not decode makes the machine formats exit 1 where Markdown
|
|
326
386
|
still renders
|
|
327
387
|
- Git subprocess output: 16 MiB (bounded while reading)
|
|
388
|
+
- Git status preflight: at most 128 repositories, including initialized nested submodules
|
|
328
389
|
- Candidate file validation: 16 MiB hard read bound
|
|
329
390
|
|
|
330
391
|
Truncation occurs only at complete UTF-8 and line boundaries and is marked explicitly.
|
|
@@ -348,6 +409,13 @@ the remote.
|
|
|
348
409
|
this is normal after cloning an empty remote.
|
|
349
410
|
- `Ahead / behind: not available` means the commit relationship cannot currently be computed.
|
|
350
411
|
Counts are reported only when the configured upstream commit is available locally.
|
|
412
|
+
- Executable Git clean/process filters can run during `status`, even with optional locks
|
|
413
|
+
disabled. Before reading status, the loader checks effective repository configuration,
|
|
414
|
+
including includes, worktree configuration and initialized nested submodules. A non-empty
|
|
415
|
+
clean/process command causes a safe error with no stdout, even if no current attribute uses
|
|
416
|
+
it. Disabling a driver silently would change cleanliness semantics. Filter-dependent repositories
|
|
417
|
+
(including locally configured Git LFS drivers) therefore need ordinary Git inspection outside
|
|
418
|
+
this loader. Git protocols are also disabled to prevent lazy object retrieval.
|
|
351
419
|
|
|
352
420
|
## Exit Codes
|
|
353
421
|
|
|
@@ -360,7 +428,7 @@ or candidate-file content.
|
|
|
360
428
|
|
|
361
429
|
## Not Included
|
|
362
430
|
|
|
363
|
-
Version 1.3.
|
|
431
|
+
Version 1.3.3 does not provide AI summaries, project-type detection, loading or transport of
|
|
364
432
|
nested `AGENTS.md` contents (the JSON `nested_context` field reports bounded existence only),
|
|
365
433
|
Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
|
|
366
434
|
network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
|
|
@@ -373,6 +441,17 @@ Run the complete local check:
|
|
|
373
441
|
just check
|
|
374
442
|
```
|
|
375
443
|
|
|
444
|
+
The lockfile uses public PyPI artifact URLs; no organization mirror or local Renovate preset
|
|
445
|
+
is needed. Renovate keeps scheduled, rate-limited updates and manual merges using a public
|
|
446
|
+
recommended preset plus the repository's explicit settings. Python 3.12 remains the tested
|
|
447
|
+
runtime; widening that range requires testing those interpreters first.
|
|
448
|
+
|
|
449
|
+
Git test fixtures discard inherited `GIT_*` routing/configuration before creating repositories.
|
|
450
|
+
Permission-sensitive tests must run as a non-root user. Gitea uses `just ci-check`: non-root
|
|
451
|
+
runners execute directly; root containers use `runuser` with an existing account named by
|
|
452
|
+
`CONTEXT_LOADER_CI_USER` (default `ubuntu`). That account must be able to execute the installed
|
|
453
|
+
`uv`, Python and `just`. No `sudo` dependency is required for this CI step.
|
|
454
|
+
|
|
376
455
|
## Version maintenance
|
|
377
456
|
|
|
378
457
|
The versions in `pyproject.toml` and `context_loader/__init__.py`, the `tool_version` in
|
|
@@ -393,11 +472,17 @@ only verifies the public release, mirrors its exact formal tag object if missing
|
|
|
393
472
|
the Gitea Release record. It builds no package and has no PyPI publishing credentials.
|
|
394
473
|
No private endpoint or credential is needed on GitHub.
|
|
395
474
|
|
|
475
|
+
For environments that permit direct public reads but share an exhausted proxy quota,
|
|
476
|
+
set the Gitea Actions repository variable `RELEASE_PUBLIC_API_DIRECT=true`. The record
|
|
477
|
+
step appends only public GitHub API/PyPI hosts to the existing proxy bypass lists. Without
|
|
478
|
+
this explicit option, the workflow uses its inherited proxy settings.
|
|
479
|
+
|
|
396
480
|
Release checks use Python 3.12, Git, and the runner's existing OpenSSL CLI. They compare downloaded
|
|
397
481
|
PyPI file bytes, metadata and SHA-256, plus the publisher/tag/commit claims in PyPI's HTTPS-served
|
|
398
482
|
provenance. This is an identity check, not an independent cryptographic Sigstore verifier.
|
|
399
483
|
|
|
400
484
|
- Tag/version or expected-commit mismatch and `just check` failure stop before build/upload.
|
|
485
|
+
- Annotated tag headers must match the exact tag name and commit, without extra or duplicate headers.
|
|
401
486
|
- The build refuses to run unless `dist/` is empty, and refuses any produced file set other than
|
|
402
487
|
the current version's wheel and sdist. A leftover artifact can carry a release filename while
|
|
403
488
|
holding different bytes, so it is never treated as the current release.
|