context-loader 1.1.0__tar.gz → 1.3.0__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.1.0 → context_loader-1.3.0}/PKG-INFO +98 -33
- {context_loader-1.1.0 → context_loader-1.3.0}/README.md +97 -32
- {context_loader-1.1.0 → context_loader-1.3.0}/context_loader/__init__.py +1 -1
- {context_loader-1.1.0 → context_loader-1.3.0}/context_loader/application.py +36 -9
- {context_loader-1.1.0 → context_loader-1.3.0}/context_loader/cli.py +11 -7
- {context_loader-1.1.0 → context_loader-1.3.0}/context_loader/collect.py +94 -0
- {context_loader-1.1.0 → context_loader-1.3.0}/pyproject.toml +1 -1
- {context_loader-1.1.0 → context_loader-1.3.0}/pyproject.toml.orig +1 -1
- {context_loader-1.1.0 → context_loader-1.3.0}/tool_cli_contract.json +5 -3
- {context_loader-1.1.0 → context_loader-1.3.0}/LICENSE +0 -0
- {context_loader-1.1.0 → context_loader-1.3.0}/context_loader/git.py +0 -0
- {context_loader-1.1.0 → context_loader-1.3.0}/context_loader/render.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: context-loader
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Render deterministic local Git context for any executor
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -219,15 +219,14 @@ Python standard library.
|
|
|
219
219
|
|
|
220
220
|
## Open-source quick start
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
rendering stable Markdown or JSON output.
|
|
222
|
+
Current stable release: **1.3.0**. Local CLI for coding-agent workflows; renders deterministic Markdown or JSON.
|
|
224
223
|
|
|
225
224
|
```bash
|
|
226
|
-
uv tool install context-loader
|
|
225
|
+
uv tool install 'context-loader==1.3.0'
|
|
227
226
|
```
|
|
228
227
|
|
|
229
228
|
```bash
|
|
230
|
-
pip install context-loader
|
|
229
|
+
pip install 'context-loader==1.3.0'
|
|
231
230
|
```
|
|
232
231
|
|
|
233
232
|
```bash
|
|
@@ -240,21 +239,21 @@ project-context --repo /path/to/repo --format json
|
|
|
240
239
|
|
|
241
240
|
## Install
|
|
242
241
|
|
|
243
|
-
Source version: `1.
|
|
242
|
+
Source version: `1.3.0`. Install its matching published release or an exact source commit.
|
|
244
243
|
|
|
245
244
|
Install via `uv`:
|
|
246
245
|
|
|
247
246
|
```bash
|
|
248
|
-
uv tool install context-loader
|
|
247
|
+
uv tool install 'context-loader==1.3.0'
|
|
249
248
|
```
|
|
250
249
|
|
|
251
250
|
Install via `pip`:
|
|
252
251
|
|
|
253
252
|
```bash
|
|
254
|
-
pip install context-loader
|
|
253
|
+
pip install 'context-loader==1.3.0'
|
|
255
254
|
```
|
|
256
255
|
|
|
257
|
-
For development or source-based installs tracking current repository (`1.
|
|
256
|
+
For development or source-based installs tracking current repository (`1.3.0`):
|
|
258
257
|
|
|
259
258
|
```bash
|
|
260
259
|
uv tool install git+https://github.com/xuanheng-tech/context-loader.git
|
|
@@ -265,8 +264,8 @@ The repository also retains `./project-context` as a direct development entry po
|
|
|
265
264
|
### Distributions
|
|
266
265
|
|
|
267
266
|
The sole distribution is **`context-loader`**, containing the `context_loader` runtime
|
|
268
|
-
and the `project-context` console script. Version `1.0.0`
|
|
269
|
-
heading contract
|
|
267
|
+
and the `project-context` console script. Version `1.0.0` broke the CLI name and Markdown
|
|
268
|
+
heading contract while leaving the then-current JSON schema unchanged. Terminal and any executor call this same
|
|
270
269
|
entry point with explicit repository and focus arguments; no provider adapter or private
|
|
271
270
|
session state is involved.
|
|
272
271
|
|
|
@@ -305,16 +304,17 @@ project-context --version
|
|
|
305
304
|
project-context --repo /home/user/projects/example
|
|
306
305
|
project-context --repo /home/user/projects/example --format markdown
|
|
307
306
|
project-context --repo /home/user/projects/example --format json
|
|
307
|
+
project-context --repo /home/user/projects/example --format json-compact
|
|
308
308
|
project-context --repo /home/user/projects/example \
|
|
309
309
|
--focus "Authentication and session management" \
|
|
310
310
|
--path auth/session.py
|
|
311
311
|
```
|
|
312
312
|
|
|
313
313
|
`--format` defaults to `markdown`. In Markdown mode, `--repo` retains the 0.1.1 contract: it must be
|
|
314
|
-
the absolute, canonical root of a non-bare Git working tree. In
|
|
315
|
-
directory inside the working tree is accepted; symlinks are normalized and the
|
|
316
|
-
reported as `canonical_root`. Relative paths, non-Git directories, regular files,
|
|
317
|
-
repositories are rejected in
|
|
314
|
+
the absolute, canonical root of a non-bare Git working tree. In the `json` and `json-compact` modes,
|
|
315
|
+
an absolute existing directory inside the working tree is accepted; symlinks are normalized and the
|
|
316
|
+
discovered root is reported as `canonical_root`. Relative paths, non-Git directories, regular files,
|
|
317
|
+
and bare repositories are rejected in every mode.
|
|
318
318
|
|
|
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.
|
|
@@ -337,15 +337,17 @@ The output has no generated timestamp, AI summary, architecture inference, or di
|
|
|
337
337
|
|
|
338
338
|
## JSON Output
|
|
339
339
|
|
|
340
|
-
`--format json` writes exactly one compact UTF-8 JSON document plus one trailing newline
|
|
341
|
-
Keys are serialized in sorted order with `ensure_ascii=False`.
|
|
340
|
+
`--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:
|
|
342
344
|
|
|
343
345
|
```json
|
|
344
346
|
{
|
|
345
|
-
"schema_version":
|
|
347
|
+
"schema_version": 3,
|
|
346
348
|
"tool": {
|
|
347
349
|
"name": "context-loader",
|
|
348
|
-
"version": "1.
|
|
350
|
+
"version": "1.3.0"
|
|
349
351
|
},
|
|
350
352
|
"repository": {
|
|
351
353
|
"requested_path": "/canonical/requested/path",
|
|
@@ -375,24 +377,78 @@ Keys are serialized in sorted order with `ensure_ascii=False`. The declared cont
|
|
|
375
377
|
"context": "the same assembled Markdown context",
|
|
376
378
|
"context_sha256": "sha256-hex",
|
|
377
379
|
"statuses": [],
|
|
380
|
+
"nested_context": {
|
|
381
|
+
"files": ["docs/AGENTS.md"],
|
|
382
|
+
"list_truncated": false,
|
|
383
|
+
"scan_truncated": false
|
|
384
|
+
},
|
|
378
385
|
"warnings": []
|
|
379
386
|
}
|
|
380
387
|
```
|
|
381
388
|
|
|
382
389
|
`context_sha256` hashes the UTF-8 bytes of `context`; each `content_sha256` does the same for that
|
|
383
|
-
source's `content
|
|
384
|
-
|
|
385
|
-
`
|
|
386
|
-
|
|
390
|
+
source's `content` body as rendered (after newline normalization, any `AGENTS.md` section selection
|
|
391
|
+
and any truncation marker) — never for the raw bytes of the file at `path`, which match only for
|
|
392
|
+
unselected, untruncated LF files. `sources` contains only file bodies that actually enter the final
|
|
393
|
+
context, in assembly order, after the existing newline normalization and truncation rules. `scope`
|
|
394
|
+
distinguishes `repository` from `global`; version 1.0.0's fixed root-file selection currently emits
|
|
395
|
+
only `repository` sources and does not add any global-file discovery.
|
|
387
396
|
|
|
388
|
-
`statuses` lists machine-readable collection and render conditions that previously appeared only inside `context` Markdown: skipped or absent sources, truncated sources, unreadable directory-tree entries, sections omitted under the global output budget,
|
|
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.
|
|
389
398
|
|
|
390
399
|
The optional `selection` object is present only on a rendered `AGENTS.md` source. Its section entries
|
|
391
400
|
contain heading, heading level, and fixed selection reasons; it never contains the original focus or
|
|
392
|
-
target path. Existing source fields
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
401
|
+
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.
|
|
403
|
+
|
|
404
|
+
`nested_context` is always present and existence-only: `files` lists repository-relative paths of
|
|
405
|
+
non-directory `AGENTS.md` entries under subdirectories, found by a bounded scan that enumerates
|
|
406
|
+
directory entries, never opens or reads a candidate file and never traverses a symlink; it carries
|
|
407
|
+
no sizes, no mtimes and no contents. A symlinked (even dangling) `AGENTS.md` is listed, because its
|
|
408
|
+
existence comes from the directory entry itself; the scan never resolves what it points at. Any
|
|
409
|
+
`.git`, `.venv`, `venv`, `node_modules` or `site-packages` directory is skipped at every depth:
|
|
410
|
+
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
|
|
416
|
+
`nested_agents_list_truncated` and `nested_agents_scan_truncated` status entries mirror both flags.
|
|
417
|
+
Presence is not instruction: whether a nested file applies, and its text, remain the caller's
|
|
418
|
+
judgment; Markdown output is unchanged.
|
|
419
|
+
|
|
420
|
+
### Compact model consumption (`--format json-compact`)
|
|
421
|
+
|
|
422
|
+
`--format json-compact` emits a `schema_version` 4 document: exactly the version-3 document with
|
|
423
|
+
`sources[*].content` omitted. Every other field — `context`, `context_sha256`, `statuses`,
|
|
424
|
+
`nested_context`, `warnings`, `tool`, `repository`, and each source's `ordinal`, `kind`, `scope`,
|
|
425
|
+
`path`, `content_sha256` and optional `selection` — is identical to `--format json` for the same
|
|
426
|
+
arguments.
|
|
427
|
+
`sources` is provenance and index metadata, not the place a body must be fetched from: every omitted
|
|
428
|
+
body is already inside `context` verbatim, so consumers should read `context` for rule text and
|
|
429
|
+
reopen a source file only when the rendered body was truncated or selected away — never to recover a
|
|
430
|
+
body that the document already carried.
|
|
431
|
+
The projection exists because the selected source bodies already occur verbatim inside `context`,
|
|
432
|
+
so carrying them again in `sources` duplicates a large share of the document bytes (measured up to
|
|
433
|
+
~44% on real worktrees, scaling with how much of `context` those bodies occupy) for a model
|
|
434
|
+
consumer without adding information. Nothing is lost: each omitted body remains inside `context`,
|
|
435
|
+
`content_sha256` still fingerprints that rendered body, and `path` plus `canonical_root` locate the
|
|
436
|
+
underlying file for a bounded manual re-read (whose raw bytes may differ from the rendered body as
|
|
437
|
+
defined above). `schema_version` is an exact document selector, not an upgrade marker: version 4 is
|
|
438
|
+
a field-set projection of version 3, so consumers must branch on the value and must not apply
|
|
439
|
+
`>=`-superset reasoning. Introducing this compact format does
|
|
440
|
+
not otherwise change default Markdown bytes, exit codes or determinism.
|
|
441
|
+
|
|
442
|
+
One historical exception is acknowledged: releases 1.0.0 and 1.1.0 both emitted `schema_version` 1
|
|
443
|
+
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.
|
|
445
|
+
|
|
446
|
+
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
|
|
448
|
+
and 2 are the exact shapes already published in release 1.2.0 and are never reused: because
|
|
449
|
+
`nested_context` changes the default document's key set, version 3 names the current full-document
|
|
450
|
+
key set and version 4 names the compact projection.
|
|
451
|
+
Callers must depend only on fields declared above.
|
|
396
452
|
The document contains no generated time or random identifier, so unchanged input produces identical
|
|
397
453
|
JSON bytes. On failure, stdout remains empty and stderr contains only a short diagnostic.
|
|
398
454
|
|
|
@@ -406,7 +462,9 @@ Resolving an instruction hierarchy stays with the calling agent harness. That in
|
|
|
406
462
|
shared or user-level instruction file outside the repository, nested or scoped `AGENTS.md` files
|
|
407
463
|
under subdirectories, and any include or import directive written inside an instruction file:
|
|
408
464
|
such a directive is transported as literal text and is never followed. `--path` selects sections
|
|
409
|
-
of the repository-root `AGENTS.md` only; it never changes which files are read.
|
|
465
|
+
of the repository-root `AGENTS.md` only; it never changes which files are read. The JSON-only
|
|
466
|
+
`nested_context` field reports the existence of nested `AGENTS.md` paths without reading them;
|
|
467
|
+
deciding whether any of them applies stays with the calling agent harness exactly as before.
|
|
410
468
|
|
|
411
469
|
A successful run therefore proves that the bounded root context was collected and rendered. It
|
|
412
470
|
does not prove that every instruction applicable to a task has been loaded.
|
|
@@ -447,6 +505,8 @@ scan that ends before EOF still reports the characters it could not select.
|
|
|
447
505
|
- All entry-file bodies: 24 KiB
|
|
448
506
|
- Declared commands: 8 KiB
|
|
449
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;
|
|
509
|
+
file contents are never read
|
|
450
510
|
- Working-tree changes: 100 paths and 4 KiB
|
|
451
511
|
- Recent commits: 8
|
|
452
512
|
- Git subprocess output: 16 MiB (bounded while reading)
|
|
@@ -485,7 +545,8 @@ or candidate-file content.
|
|
|
485
545
|
|
|
486
546
|
## Not Included
|
|
487
547
|
|
|
488
|
-
Version 1.
|
|
548
|
+
Version 1.3.0 does not provide AI summaries, project-type detection, loading or transport of
|
|
549
|
+
nested `AGENTS.md` contents (the JSON `nested_context` field reports bounded existence only),
|
|
489
550
|
Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
|
|
490
551
|
network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
|
|
491
552
|
|
|
@@ -499,9 +560,13 @@ just check
|
|
|
499
560
|
|
|
500
561
|
## Version maintenance
|
|
501
562
|
|
|
502
|
-
The versions in `pyproject.toml` and `context_loader/__init__.py`, the
|
|
503
|
-
|
|
504
|
-
|
|
563
|
+
The versions in `pyproject.toml` and `context_loader/__init__.py`, the `tool_version` in
|
|
564
|
+
`tool_cli_contract.json`, the matching `CHANGELOG.md` section, required tests, and the README
|
|
565
|
+
current-stable declaration plus `context-loader==X.Y.Z`
|
|
566
|
+
install pins must change in the same release-preparation batch. `CHANGELOG.md` is the
|
|
567
|
+
authoritative version-change record. `scripts/release.py` rejects README stable/install
|
|
568
|
+
pins that disagree with the intended package version; historical changelog entries are
|
|
569
|
+
not part of that check. Merging to `master` is not a release; formal publication
|
|
505
570
|
still requires a separately created and pushed tag.
|
|
506
571
|
|
|
507
572
|
## Release closure and retries
|
|
@@ -8,15 +8,14 @@ Python standard library.
|
|
|
8
8
|
|
|
9
9
|
## Open-source quick start
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
rendering stable Markdown or JSON output.
|
|
11
|
+
Current stable release: **1.3.0**. Local CLI for coding-agent workflows; renders deterministic Markdown or JSON.
|
|
13
12
|
|
|
14
13
|
```bash
|
|
15
|
-
uv tool install context-loader
|
|
14
|
+
uv tool install 'context-loader==1.3.0'
|
|
16
15
|
```
|
|
17
16
|
|
|
18
17
|
```bash
|
|
19
|
-
pip install context-loader
|
|
18
|
+
pip install 'context-loader==1.3.0'
|
|
20
19
|
```
|
|
21
20
|
|
|
22
21
|
```bash
|
|
@@ -29,21 +28,21 @@ project-context --repo /path/to/repo --format json
|
|
|
29
28
|
|
|
30
29
|
## Install
|
|
31
30
|
|
|
32
|
-
Source version: `1.
|
|
31
|
+
Source version: `1.3.0`. Install its matching published release or an exact source commit.
|
|
33
32
|
|
|
34
33
|
Install via `uv`:
|
|
35
34
|
|
|
36
35
|
```bash
|
|
37
|
-
uv tool install context-loader
|
|
36
|
+
uv tool install 'context-loader==1.3.0'
|
|
38
37
|
```
|
|
39
38
|
|
|
40
39
|
Install via `pip`:
|
|
41
40
|
|
|
42
41
|
```bash
|
|
43
|
-
pip install context-loader
|
|
42
|
+
pip install 'context-loader==1.3.0'
|
|
44
43
|
```
|
|
45
44
|
|
|
46
|
-
For development or source-based installs tracking current repository (`1.
|
|
45
|
+
For development or source-based installs tracking current repository (`1.3.0`):
|
|
47
46
|
|
|
48
47
|
```bash
|
|
49
48
|
uv tool install git+https://github.com/xuanheng-tech/context-loader.git
|
|
@@ -54,8 +53,8 @@ The repository also retains `./project-context` as a direct development entry po
|
|
|
54
53
|
### Distributions
|
|
55
54
|
|
|
56
55
|
The sole distribution is **`context-loader`**, containing the `context_loader` runtime
|
|
57
|
-
and the `project-context` console script. Version `1.0.0`
|
|
58
|
-
heading contract
|
|
56
|
+
and the `project-context` console script. Version `1.0.0` broke the CLI name and Markdown
|
|
57
|
+
heading contract while leaving the then-current JSON schema unchanged. Terminal and any executor call this same
|
|
59
58
|
entry point with explicit repository and focus arguments; no provider adapter or private
|
|
60
59
|
session state is involved.
|
|
61
60
|
|
|
@@ -94,16 +93,17 @@ project-context --version
|
|
|
94
93
|
project-context --repo /home/user/projects/example
|
|
95
94
|
project-context --repo /home/user/projects/example --format markdown
|
|
96
95
|
project-context --repo /home/user/projects/example --format json
|
|
96
|
+
project-context --repo /home/user/projects/example --format json-compact
|
|
97
97
|
project-context --repo /home/user/projects/example \
|
|
98
98
|
--focus "Authentication and session management" \
|
|
99
99
|
--path auth/session.py
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
`--format` defaults to `markdown`. In Markdown mode, `--repo` retains the 0.1.1 contract: it must be
|
|
103
|
-
the absolute, canonical root of a non-bare Git working tree. In
|
|
104
|
-
directory inside the working tree is accepted; symlinks are normalized and the
|
|
105
|
-
reported as `canonical_root`. Relative paths, non-Git directories, regular files,
|
|
106
|
-
repositories are rejected in
|
|
103
|
+
the absolute, canonical root of a non-bare Git working tree. In the `json` and `json-compact` modes,
|
|
104
|
+
an absolute existing directory inside the working tree is accepted; symlinks are normalized and the
|
|
105
|
+
discovered root is reported as `canonical_root`. Relative paths, non-Git directories, regular files,
|
|
106
|
+
and bare repositories are rejected in every mode.
|
|
107
107
|
|
|
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.
|
|
@@ -126,15 +126,17 @@ The output has no generated timestamp, AI summary, architecture inference, or di
|
|
|
126
126
|
|
|
127
127
|
## JSON Output
|
|
128
128
|
|
|
129
|
-
`--format json` writes exactly one compact UTF-8 JSON document plus one trailing newline
|
|
130
|
-
Keys are serialized in sorted order with `ensure_ascii=False`.
|
|
129
|
+
`--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:
|
|
131
133
|
|
|
132
134
|
```json
|
|
133
135
|
{
|
|
134
|
-
"schema_version":
|
|
136
|
+
"schema_version": 3,
|
|
135
137
|
"tool": {
|
|
136
138
|
"name": "context-loader",
|
|
137
|
-
"version": "1.
|
|
139
|
+
"version": "1.3.0"
|
|
138
140
|
},
|
|
139
141
|
"repository": {
|
|
140
142
|
"requested_path": "/canonical/requested/path",
|
|
@@ -164,24 +166,78 @@ Keys are serialized in sorted order with `ensure_ascii=False`. The declared cont
|
|
|
164
166
|
"context": "the same assembled Markdown context",
|
|
165
167
|
"context_sha256": "sha256-hex",
|
|
166
168
|
"statuses": [],
|
|
169
|
+
"nested_context": {
|
|
170
|
+
"files": ["docs/AGENTS.md"],
|
|
171
|
+
"list_truncated": false,
|
|
172
|
+
"scan_truncated": false
|
|
173
|
+
},
|
|
167
174
|
"warnings": []
|
|
168
175
|
}
|
|
169
176
|
```
|
|
170
177
|
|
|
171
178
|
`context_sha256` hashes the UTF-8 bytes of `context`; each `content_sha256` does the same for that
|
|
172
|
-
source's `content
|
|
173
|
-
|
|
174
|
-
`
|
|
175
|
-
|
|
179
|
+
source's `content` body as rendered (after newline normalization, any `AGENTS.md` section selection
|
|
180
|
+
and any truncation marker) — never for the raw bytes of the file at `path`, which match only for
|
|
181
|
+
unselected, untruncated LF files. `sources` contains only file bodies that actually enter the final
|
|
182
|
+
context, in assembly order, after the existing newline normalization and truncation rules. `scope`
|
|
183
|
+
distinguishes `repository` from `global`; version 1.0.0's fixed root-file selection currently emits
|
|
184
|
+
only `repository` sources and does not add any global-file discovery.
|
|
176
185
|
|
|
177
|
-
`statuses` lists machine-readable collection and render conditions that previously appeared only inside `context` Markdown: skipped or absent sources, truncated sources, unreadable directory-tree entries, sections omitted under the global output budget,
|
|
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.
|
|
178
187
|
|
|
179
188
|
The optional `selection` object is present only on a rendered `AGENTS.md` source. Its section entries
|
|
180
189
|
contain heading, heading level, and fixed selection reasons; it never contains the original focus or
|
|
181
|
-
target path. Existing source fields
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
190
|
+
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.
|
|
192
|
+
|
|
193
|
+
`nested_context` is always present and existence-only: `files` lists repository-relative paths of
|
|
194
|
+
non-directory `AGENTS.md` entries under subdirectories, found by a bounded scan that enumerates
|
|
195
|
+
directory entries, never opens or reads a candidate file and never traverses a symlink; it carries
|
|
196
|
+
no sizes, no mtimes and no contents. A symlinked (even dangling) `AGENTS.md` is listed, because its
|
|
197
|
+
existence comes from the directory entry itself; the scan never resolves what it points at. Any
|
|
198
|
+
`.git`, `.venv`, `venv`, `node_modules` or `site-packages` directory is skipped at every depth:
|
|
199
|
+
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
|
|
205
|
+
`nested_agents_list_truncated` and `nested_agents_scan_truncated` status entries mirror both flags.
|
|
206
|
+
Presence is not instruction: whether a nested file applies, and its text, remain the caller's
|
|
207
|
+
judgment; Markdown output is unchanged.
|
|
208
|
+
|
|
209
|
+
### Compact model consumption (`--format json-compact`)
|
|
210
|
+
|
|
211
|
+
`--format json-compact` emits a `schema_version` 4 document: exactly the version-3 document with
|
|
212
|
+
`sources[*].content` omitted. Every other field — `context`, `context_sha256`, `statuses`,
|
|
213
|
+
`nested_context`, `warnings`, `tool`, `repository`, and each source's `ordinal`, `kind`, `scope`,
|
|
214
|
+
`path`, `content_sha256` and optional `selection` — is identical to `--format json` for the same
|
|
215
|
+
arguments.
|
|
216
|
+
`sources` is provenance and index metadata, not the place a body must be fetched from: every omitted
|
|
217
|
+
body is already inside `context` verbatim, so consumers should read `context` for rule text and
|
|
218
|
+
reopen a source file only when the rendered body was truncated or selected away — never to recover a
|
|
219
|
+
body that the document already carried.
|
|
220
|
+
The projection exists because the selected source bodies already occur verbatim inside `context`,
|
|
221
|
+
so carrying them again in `sources` duplicates a large share of the document bytes (measured up to
|
|
222
|
+
~44% on real worktrees, scaling with how much of `context` those bodies occupy) for a model
|
|
223
|
+
consumer without adding information. Nothing is lost: each omitted body remains inside `context`,
|
|
224
|
+
`content_sha256` still fingerprints that rendered body, and `path` plus `canonical_root` locate the
|
|
225
|
+
underlying file for a bounded manual re-read (whose raw bytes may differ from the rendered body as
|
|
226
|
+
defined above). `schema_version` is an exact document selector, not an upgrade marker: version 4 is
|
|
227
|
+
a field-set projection of version 3, so consumers must branch on the value and must not apply
|
|
228
|
+
`>=`-superset reasoning. Introducing this compact format does
|
|
229
|
+
not otherwise change default Markdown bytes, exit codes or determinism.
|
|
230
|
+
|
|
231
|
+
One historical exception is acknowledged: releases 1.0.0 and 1.1.0 both emitted `schema_version` 1
|
|
232
|
+
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.
|
|
234
|
+
|
|
235
|
+
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
|
|
237
|
+
and 2 are the exact shapes already published in release 1.2.0 and are never reused: because
|
|
238
|
+
`nested_context` changes the default document's key set, version 3 names the current full-document
|
|
239
|
+
key set and version 4 names the compact projection.
|
|
240
|
+
Callers must depend only on fields declared above.
|
|
185
241
|
The document contains no generated time or random identifier, so unchanged input produces identical
|
|
186
242
|
JSON bytes. On failure, stdout remains empty and stderr contains only a short diagnostic.
|
|
187
243
|
|
|
@@ -195,7 +251,9 @@ Resolving an instruction hierarchy stays with the calling agent harness. That in
|
|
|
195
251
|
shared or user-level instruction file outside the repository, nested or scoped `AGENTS.md` files
|
|
196
252
|
under subdirectories, and any include or import directive written inside an instruction file:
|
|
197
253
|
such a directive is transported as literal text and is never followed. `--path` selects sections
|
|
198
|
-
of the repository-root `AGENTS.md` only; it never changes which files are read.
|
|
254
|
+
of the repository-root `AGENTS.md` only; it never changes which files are read. The JSON-only
|
|
255
|
+
`nested_context` field reports the existence of nested `AGENTS.md` paths without reading them;
|
|
256
|
+
deciding whether any of them applies stays with the calling agent harness exactly as before.
|
|
199
257
|
|
|
200
258
|
A successful run therefore proves that the bounded root context was collected and rendered. It
|
|
201
259
|
does not prove that every instruction applicable to a task has been loaded.
|
|
@@ -236,6 +294,8 @@ scan that ends before EOF still reports the characters it could not select.
|
|
|
236
294
|
- All entry-file bodies: 24 KiB
|
|
237
295
|
- Declared commands: 8 KiB
|
|
238
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;
|
|
298
|
+
file contents are never read
|
|
239
299
|
- Working-tree changes: 100 paths and 4 KiB
|
|
240
300
|
- Recent commits: 8
|
|
241
301
|
- Git subprocess output: 16 MiB (bounded while reading)
|
|
@@ -274,7 +334,8 @@ or candidate-file content.
|
|
|
274
334
|
|
|
275
335
|
## Not Included
|
|
276
336
|
|
|
277
|
-
Version 1.
|
|
337
|
+
Version 1.3.0 does not provide AI summaries, project-type detection, loading or transport of
|
|
338
|
+
nested `AGENTS.md` contents (the JSON `nested_context` field reports bounded existence only),
|
|
278
339
|
Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
|
|
279
340
|
network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
|
|
280
341
|
|
|
@@ -288,9 +349,13 @@ just check
|
|
|
288
349
|
|
|
289
350
|
## Version maintenance
|
|
290
351
|
|
|
291
|
-
The versions in `pyproject.toml` and `context_loader/__init__.py`, the
|
|
292
|
-
|
|
293
|
-
|
|
352
|
+
The versions in `pyproject.toml` and `context_loader/__init__.py`, the `tool_version` in
|
|
353
|
+
`tool_cli_contract.json`, the matching `CHANGELOG.md` section, required tests, and the README
|
|
354
|
+
current-stable declaration plus `context-loader==X.Y.Z`
|
|
355
|
+
install pins must change in the same release-preparation batch. `CHANGELOG.md` is the
|
|
356
|
+
authoritative version-change record. `scripts/release.py` rejects README stable/install
|
|
357
|
+
pins that disagree with the intended package version; historical changelog entries are
|
|
358
|
+
not part of that check. Merging to `master` is not a release; formal publication
|
|
294
359
|
still requires a separately created and pushed tag.
|
|
295
360
|
|
|
296
361
|
## Release closure and retries
|
|
@@ -20,13 +20,18 @@ from .collect import (
|
|
|
20
20
|
AgentsSelectionAudit,
|
|
21
21
|
AgentsSelectionInputError,
|
|
22
22
|
CollectedFile,
|
|
23
|
+
NestedContextPresence,
|
|
23
24
|
ProjectContext,
|
|
24
25
|
collect_project_context,
|
|
25
26
|
)
|
|
26
27
|
from .git import ContextLoaderError, collect_repository
|
|
27
|
-
from .render import render_markdown_with_details, rendered_source_contents
|
|
28
|
+
from .render import _display, render_markdown_with_details, rendered_source_contents
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
# Each value names one exact document shape and is never reused: 1 and 2 are the
|
|
31
|
+
# shapes published in release 1.2.0, so adding the nested_context object to both
|
|
32
|
+
# formats moved the default document to 3 and the compact projection to 4.
|
|
33
|
+
JSON_SCHEMA_VERSION = 3
|
|
34
|
+
COMPACT_JSON_SCHEMA_VERSION = 4
|
|
30
35
|
TOOL_NAME = "context-loader"
|
|
31
36
|
|
|
32
37
|
_STATUS_CODE_BY_MESSAGE = {
|
|
@@ -80,6 +85,7 @@ class ProjectContextResult:
|
|
|
80
85
|
context_sha256: str
|
|
81
86
|
warnings: tuple[str, ...]
|
|
82
87
|
statuses: tuple[ContextStatus, ...]
|
|
88
|
+
nested_context: NestedContextPresence
|
|
83
89
|
|
|
84
90
|
|
|
85
91
|
def _text_sha256(content: str) -> str:
|
|
@@ -184,11 +190,16 @@ def _build_statuses(
|
|
|
184
190
|
for entry in project.directory_tree.entries:
|
|
185
191
|
if entry.kind == "unreadable_directory":
|
|
186
192
|
subject = entry.path if entry.path else "."
|
|
187
|
-
statuses.append(_status("unreadable", "tree_entry", subject))
|
|
193
|
+
statuses.append(_status("unreadable", "tree_entry", _display(subject)))
|
|
188
194
|
|
|
189
195
|
for title in omitted_sections:
|
|
190
196
|
statuses.append(_status("section_omitted", "section", title))
|
|
191
197
|
|
|
198
|
+
if project.nested_context.list_truncated:
|
|
199
|
+
statuses.append(_status("nested_agents_list_truncated", "nested_context", "AGENTS.md"))
|
|
200
|
+
if project.nested_context.scan_truncated:
|
|
201
|
+
statuses.append(_status("nested_agents_scan_truncated", "nested_context", "AGENTS.md"))
|
|
202
|
+
|
|
192
203
|
deduped: list[ContextStatus] = []
|
|
193
204
|
seen: set[tuple[str, str, str]] = set()
|
|
194
205
|
for status in statuses:
|
|
@@ -238,6 +249,7 @@ def load_project_context(
|
|
|
238
249
|
context_sha256=hashlib.sha256(rendered.output).hexdigest(),
|
|
239
250
|
warnings=(),
|
|
240
251
|
statuses=statuses,
|
|
252
|
+
nested_context=project.nested_context,
|
|
241
253
|
)
|
|
242
254
|
|
|
243
255
|
|
|
@@ -272,24 +284,30 @@ def _status_document(status: ContextStatus) -> dict[str, object]:
|
|
|
272
284
|
}
|
|
273
285
|
|
|
274
286
|
|
|
275
|
-
def _source_document(source: ProjectContextSource) -> dict[str, object]:
|
|
287
|
+
def _source_document(source: ProjectContextSource, *, include_content: bool) -> dict[str, object]:
|
|
276
288
|
document: dict[str, object] = {
|
|
277
289
|
"ordinal": source.ordinal,
|
|
278
290
|
"kind": source.kind,
|
|
279
291
|
"scope": source.scope,
|
|
280
292
|
"path": os.fspath(source.path),
|
|
281
293
|
"content_sha256": source.content_sha256,
|
|
282
|
-
"content": source.content,
|
|
283
294
|
}
|
|
295
|
+
if include_content:
|
|
296
|
+
document["content"] = source.content
|
|
284
297
|
if source.selection is not None:
|
|
285
298
|
document["selection"] = _selection_document(source.selection)
|
|
286
299
|
return document
|
|
287
300
|
|
|
288
301
|
|
|
289
|
-
def render_json(result: ProjectContextResult) -> bytes:
|
|
290
|
-
"""Serialize one result as stable UTF-8 JSON followed by exactly one newline.
|
|
302
|
+
def render_json(result: ProjectContextResult, *, compact: bool = False) -> bytes:
|
|
303
|
+
"""Serialize one result as stable UTF-8 JSON followed by exactly one newline.
|
|
304
|
+
|
|
305
|
+
``compact`` projects away the duplicated source bodies only at this
|
|
306
|
+
boundary: statuses and context were computed from the full model at load
|
|
307
|
+
time, so both documents agree on everything except ``sources[*].content``.
|
|
308
|
+
"""
|
|
291
309
|
document = {
|
|
292
|
-
"schema_version": result.schema_version,
|
|
310
|
+
"schema_version": (COMPACT_JSON_SCHEMA_VERSION if compact else result.schema_version),
|
|
293
311
|
"tool": {
|
|
294
312
|
"name": result.tool.name,
|
|
295
313
|
"version": result.tool.version,
|
|
@@ -298,8 +316,17 @@ def render_json(result: ProjectContextResult) -> bytes:
|
|
|
298
316
|
"requested_path": os.fspath(result.repository.requested_path),
|
|
299
317
|
"canonical_root": os.fspath(result.repository.canonical_root),
|
|
300
318
|
},
|
|
301
|
-
"sources": [
|
|
319
|
+
"sources": [
|
|
320
|
+
_source_document(source, include_content=not compact) for source in result.sources
|
|
321
|
+
],
|
|
302
322
|
"statuses": [_status_document(status) for status in result.statuses],
|
|
323
|
+
"nested_context": {
|
|
324
|
+
# Filesystem names arrive undecoded (surrogate-escaped); sanitize exactly like
|
|
325
|
+
# tree/change paths so no raw byte can reach the UTF-8 document.
|
|
326
|
+
"files": [_display(path) for path in result.nested_context.files],
|
|
327
|
+
"list_truncated": result.nested_context.list_truncated,
|
|
328
|
+
"scan_truncated": result.nested_context.scan_truncated,
|
|
329
|
+
},
|
|
303
330
|
"context": result.context,
|
|
304
331
|
"context_sha256": result.context_sha256,
|
|
305
332
|
"warnings": list(result.warnings),
|
|
@@ -36,9 +36,12 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
36
36
|
)
|
|
37
37
|
parser.add_argument(
|
|
38
38
|
"--format",
|
|
39
|
-
choices=("markdown", "json"),
|
|
39
|
+
choices=("markdown", "json", "json-compact"),
|
|
40
40
|
default="markdown",
|
|
41
|
-
help=
|
|
41
|
+
help=(
|
|
42
|
+
"output format (default: markdown); json-compact omits the "
|
|
43
|
+
"duplicated source bodies (see README, Compact model consumption)"
|
|
44
|
+
),
|
|
42
45
|
)
|
|
43
46
|
return parser
|
|
44
47
|
|
|
@@ -52,11 +55,12 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
52
55
|
focus=arguments.focus,
|
|
53
56
|
path=arguments.target_path,
|
|
54
57
|
)
|
|
55
|
-
|
|
56
|
-
result.context.encode("utf-8")
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
if arguments.format == "markdown":
|
|
59
|
+
output = result.context.encode("utf-8")
|
|
60
|
+
elif arguments.format == "json":
|
|
61
|
+
output = render_json(result)
|
|
62
|
+
else:
|
|
63
|
+
output = render_json(result, compact=True)
|
|
60
64
|
except ContextLoaderError as exc:
|
|
61
65
|
print(f"error: {exc}", file=sys.stderr)
|
|
62
66
|
return exc.exit_code
|
|
@@ -26,6 +26,13 @@ DECLARED_COMMANDS_LIMIT_BYTES = 8 * 1024
|
|
|
26
26
|
DIRECTORY_TREE_LIMIT_BYTES = 12 * 1024
|
|
27
27
|
DIRECTORY_TREE_MAX_ITEMS = 300
|
|
28
28
|
DIRECTORY_TREE_MAX_DEPTH = 2
|
|
29
|
+
NESTED_AGENTS_MAX_DEPTH = 4
|
|
30
|
+
NESTED_AGENTS_MAX_DIRECTORIES = 2_000
|
|
31
|
+
NESTED_AGENTS_MAX_FILES = 32
|
|
32
|
+
NESTED_AGENTS_MAX_LIST_BYTES = 4 * 1024
|
|
33
|
+
NESTED_AGENTS_EXCLUDED_DIRECTORIES = frozenset(
|
|
34
|
+
{".git", ".venv", "node_modules", "site-packages", "venv"}
|
|
35
|
+
)
|
|
29
36
|
FILE_SCAN_LIMIT_BYTES = 16 * 1024 * 1024
|
|
30
37
|
MAX_FILE_SCAN_BYTES = FILE_SCAN_LIMIT_BYTES
|
|
31
38
|
TRUNCATION_MARKER = "… truncated by context-loader …"
|
|
@@ -190,6 +197,15 @@ class DirectoryTree:
|
|
|
190
197
|
truncated: bool = False
|
|
191
198
|
|
|
192
199
|
|
|
200
|
+
@dataclass(frozen=True, slots=True)
|
|
201
|
+
class NestedContextPresence:
|
|
202
|
+
"""Existence-only index of nested AGENTS.md files; their contents are never read."""
|
|
203
|
+
|
|
204
|
+
files: tuple[str, ...]
|
|
205
|
+
list_truncated: bool = False
|
|
206
|
+
scan_truncated: bool = False
|
|
207
|
+
|
|
208
|
+
|
|
193
209
|
@dataclass(frozen=True, slots=True)
|
|
194
210
|
class ProjectContext:
|
|
195
211
|
instructions: CollectedFile
|
|
@@ -197,6 +213,7 @@ class ProjectContext:
|
|
|
197
213
|
entry_files: tuple[CollectedFile, ...]
|
|
198
214
|
commands: tuple[DeclaredCommand, ...]
|
|
199
215
|
directory_tree: DirectoryTree
|
|
216
|
+
nested_context: NestedContextPresence
|
|
200
217
|
|
|
201
218
|
|
|
202
219
|
@dataclass(frozen=True, slots=True)
|
|
@@ -975,6 +992,82 @@ def _collect_directory_tree(root: Path) -> DirectoryTree:
|
|
|
975
992
|
return DirectoryTree(tuple(collected), truncated)
|
|
976
993
|
|
|
977
994
|
|
|
995
|
+
def collect_nested_agents_presence(repository: Path) -> NestedContextPresence:
|
|
996
|
+
"""List nested AGENTS.md paths under a bounded, contents-blind directory scan.
|
|
997
|
+
|
|
998
|
+
The scan reads directory entries only: it never opens a candidate file, never
|
|
999
|
+
traverses a symlink, and reports truncation honestly so presence claims stay
|
|
1000
|
+
auditable. Any non-directory entry named AGENTS.md is listed, including
|
|
1001
|
+
symlinked ones, because existence comes from enumeration alone; entry types
|
|
1002
|
+
are never resolved beyond directory-vs-not, and contents remain the caller's
|
|
1003
|
+
responsibility to read.
|
|
1004
|
+
"""
|
|
1005
|
+
files: list[str] = []
|
|
1006
|
+
reported_bytes = 0
|
|
1007
|
+
state = {"list_truncated": False, "scan_truncated": False, "directories": 0}
|
|
1008
|
+
|
|
1009
|
+
def walk(directory_descriptor: int, prefix: str, depth: int) -> None:
|
|
1010
|
+
nonlocal reported_bytes
|
|
1011
|
+
if depth > NESTED_AGENTS_MAX_DEPTH or state["directories"] >= NESTED_AGENTS_MAX_DIRECTORIES:
|
|
1012
|
+
state["scan_truncated"] = True
|
|
1013
|
+
return
|
|
1014
|
+
state["directories"] += 1
|
|
1015
|
+
try:
|
|
1016
|
+
with os.scandir(directory_descriptor) as iterator:
|
|
1017
|
+
entries = sorted(iterator, key=lambda entry: entry.name)
|
|
1018
|
+
except OSError:
|
|
1019
|
+
state["scan_truncated"] = True
|
|
1020
|
+
return
|
|
1021
|
+
subdirectories: list[str] = []
|
|
1022
|
+
for entry in entries:
|
|
1023
|
+
try:
|
|
1024
|
+
is_directory = entry.is_dir(follow_symlinks=False)
|
|
1025
|
+
except OSError:
|
|
1026
|
+
state["scan_truncated"] = True
|
|
1027
|
+
continue
|
|
1028
|
+
path = f"{prefix}/{entry.name}" if prefix else entry.name
|
|
1029
|
+
if is_directory:
|
|
1030
|
+
if entry.name not in NESTED_AGENTS_EXCLUDED_DIRECTORIES:
|
|
1031
|
+
subdirectories.append(path)
|
|
1032
|
+
continue
|
|
1033
|
+
if entry.name != "AGENTS.md" or not prefix:
|
|
1034
|
+
continue
|
|
1035
|
+
if len(files) >= NESTED_AGENTS_MAX_FILES:
|
|
1036
|
+
state["list_truncated"] = True
|
|
1037
|
+
continue
|
|
1038
|
+
name_bytes = len(path.encode("utf-8", errors="surrogateescape"))
|
|
1039
|
+
name_bytes += 1 if files else 0
|
|
1040
|
+
if reported_bytes + name_bytes > NESTED_AGENTS_MAX_LIST_BYTES:
|
|
1041
|
+
state["list_truncated"] = True
|
|
1042
|
+
continue
|
|
1043
|
+
reported_bytes += name_bytes
|
|
1044
|
+
files.append(path)
|
|
1045
|
+
for path in subdirectories:
|
|
1046
|
+
flags = os.O_RDONLY | os.O_CLOEXEC | os.O_DIRECTORY | getattr(os, "O_NOFOLLOW", 0)
|
|
1047
|
+
try:
|
|
1048
|
+
child_descriptor = os.open(
|
|
1049
|
+
path.rsplit("/", 1)[-1], flags, dir_fd=directory_descriptor
|
|
1050
|
+
)
|
|
1051
|
+
except OSError:
|
|
1052
|
+
state["scan_truncated"] = True
|
|
1053
|
+
continue
|
|
1054
|
+
try:
|
|
1055
|
+
walk(child_descriptor, path, depth + 1)
|
|
1056
|
+
finally:
|
|
1057
|
+
os.close(child_descriptor)
|
|
1058
|
+
|
|
1059
|
+
flags = os.O_RDONLY | os.O_CLOEXEC | os.O_DIRECTORY | getattr(os, "O_NOFOLLOW", 0)
|
|
1060
|
+
try:
|
|
1061
|
+
root_descriptor = os.open(repository, flags)
|
|
1062
|
+
except OSError:
|
|
1063
|
+
return NestedContextPresence((), False, True)
|
|
1064
|
+
try:
|
|
1065
|
+
walk(root_descriptor, "", 0)
|
|
1066
|
+
finally:
|
|
1067
|
+
os.close(root_descriptor)
|
|
1068
|
+
return NestedContextPresence(tuple(files), state["list_truncated"], state["scan_truncated"])
|
|
1069
|
+
|
|
1070
|
+
|
|
978
1071
|
def collect_project_context(
|
|
979
1072
|
repository: Path,
|
|
980
1073
|
*,
|
|
@@ -1001,4 +1094,5 @@ def collect_project_context(
|
|
|
1001
1094
|
entry_files=entry_files,
|
|
1002
1095
|
commands=_collect_commands(entry_files),
|
|
1003
1096
|
directory_tree=_collect_directory_tree(repository),
|
|
1097
|
+
nested_context=collect_nested_agents_presence(repository),
|
|
1004
1098
|
)
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"repo_is_absolute_git_worktree",
|
|
16
16
|
"markdown_requires_canonical_root",
|
|
17
17
|
"json_accepts_path_inside_worktree",
|
|
18
|
+
"json_compact_accepts_path_inside_worktree",
|
|
18
19
|
"target_path_is_repository_relative"
|
|
19
20
|
],
|
|
20
21
|
"result_statuses": [
|
|
@@ -24,11 +25,12 @@
|
|
|
24
25
|
]
|
|
25
26
|
}
|
|
26
27
|
],
|
|
27
|
-
"contract_version":
|
|
28
|
+
"contract_version": 3,
|
|
28
29
|
"flags": {
|
|
29
30
|
"format": [
|
|
30
31
|
"markdown",
|
|
31
|
-
"json"
|
|
32
|
+
"json",
|
|
33
|
+
"json-compact"
|
|
32
34
|
],
|
|
33
35
|
"output": "stdout",
|
|
34
36
|
"stderr": "bounded_diagnostic_only"
|
|
@@ -44,5 +46,5 @@
|
|
|
44
46
|
],
|
|
45
47
|
"schema_version": 1,
|
|
46
48
|
"tool_name": "context-loader",
|
|
47
|
-
"tool_version": "1.
|
|
49
|
+
"tool_version": "1.3.0"
|
|
48
50
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|