context-loader 0.1.8__tar.gz → 1.0.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-0.1.8 → context_loader-1.0.0}/PKG-INFO +30 -27
- {context_loader-0.1.8 → context_loader-1.0.0}/README.md +28 -25
- context_loader-1.0.0/context_loader/__init__.py +3 -0
- {context_loader-0.1.8 → context_loader-1.0.0}/context_loader/cli.py +2 -2
- {context_loader-0.1.8 → context_loader-1.0.0}/context_loader/collect.py +12 -1
- {context_loader-0.1.8 → context_loader-1.0.0}/context_loader/git.py +67 -9
- {context_loader-0.1.8 → context_loader-1.0.0}/context_loader/render.py +1 -1
- {context_loader-0.1.8 → context_loader-1.0.0}/pyproject.toml +3 -3
- {context_loader-0.1.8 → context_loader-1.0.0}/pyproject.toml.orig +3 -3
- {context_loader-0.1.8 → context_loader-1.0.0}/tool_cli_contract.json +3 -3
- context_loader-0.1.8/context_loader/__init__.py +0 -3
- {context_loader-0.1.8 → context_loader-1.0.0}/LICENSE +0 -0
- {context_loader-0.1.8 → context_loader-1.0.0}/context_loader/application.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: context-loader
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Render deterministic local Git context for
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Render deterministic local Git context for any executor
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
7
7
|
http://www.apache.org/licenses/
|
|
@@ -231,16 +231,16 @@ pip install context-loader
|
|
|
231
231
|
```
|
|
232
232
|
|
|
233
233
|
```bash
|
|
234
|
-
|
|
234
|
+
project-context --repo /path/to/repo
|
|
235
235
|
```
|
|
236
236
|
|
|
237
237
|
```bash
|
|
238
|
-
|
|
238
|
+
project-context --repo /path/to/repo --format json
|
|
239
239
|
```
|
|
240
240
|
|
|
241
241
|
## Install
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
Source version: `1.0.0`. Install its matching published release or an exact source commit.
|
|
244
244
|
|
|
245
245
|
Install via `uv`:
|
|
246
246
|
|
|
@@ -254,25 +254,24 @@ Install via `pip`:
|
|
|
254
254
|
pip install context-loader
|
|
255
255
|
```
|
|
256
256
|
|
|
257
|
-
For development or source-based installs tracking current repository (`0.
|
|
257
|
+
For development or source-based installs tracking current repository (`1.0.0`):
|
|
258
258
|
|
|
259
259
|
```bash
|
|
260
260
|
uv tool install git+https://github.com/xuanheng-tech/context-loader.git
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
-
The repository also retains `./
|
|
263
|
+
The repository also retains `./project-context` as a direct development entry point.
|
|
264
264
|
|
|
265
265
|
### Distributions
|
|
266
266
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
The sole distribution is **`context-loader`**, containing the `context_loader` runtime
|
|
268
|
+
and the `project-context` console script. Version `1.0.0` breaks the CLI name and Markdown
|
|
269
|
+
heading contract. The JSON schema is unchanged. Terminal and any executor call this same
|
|
270
|
+
entry point with explicit repository and focus arguments; no provider adapter or private
|
|
271
|
+
session state is involved.
|
|
270
272
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
so installing either name yields exactly one implementation and one CLI. Existing pins keep
|
|
274
|
-
working, and releases `0.1.5`-`0.1.7` of the old name are unchanged. New integrations should
|
|
275
|
-
depend on `context-loader`.
|
|
273
|
+
Previously published packages and historical release records remain unchanged. The new
|
|
274
|
+
release publishes no compatibility distribution or legacy executable alias.
|
|
276
275
|
|
|
277
276
|
The wheel installs the runtime package only. The source distribution additionally carries
|
|
278
277
|
`tool_cli_contract.json`, so a package-only consumer can pin the declared public CLI contract
|
|
@@ -302,11 +301,11 @@ automatic redaction**. Inspect the output before sharing it with a person or ext
|
|
|
302
301
|
## Usage
|
|
303
302
|
|
|
304
303
|
```bash
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
304
|
+
project-context --version
|
|
305
|
+
project-context --repo /home/user/projects/example
|
|
306
|
+
project-context --repo /home/user/projects/example --format markdown
|
|
307
|
+
project-context --repo /home/user/projects/example --format json
|
|
308
|
+
project-context --repo /home/user/projects/example \
|
|
310
309
|
--focus "Authentication and session management" \
|
|
311
310
|
--path auth/session.py
|
|
312
311
|
```
|
|
@@ -346,7 +345,7 @@ Keys are serialized in sorted order with `ensure_ascii=False`. The declared cont
|
|
|
346
345
|
"schema_version": 1,
|
|
347
346
|
"tool": {
|
|
348
347
|
"name": "context-loader",
|
|
349
|
-
"version": "0.
|
|
348
|
+
"version": "1.0.0"
|
|
350
349
|
},
|
|
351
350
|
"repository": {
|
|
352
351
|
"requested_path": "/canonical/requested/path",
|
|
@@ -382,7 +381,7 @@ Keys are serialized in sorted order with `ensure_ascii=False`. The declared cont
|
|
|
382
381
|
`context_sha256` hashes the UTF-8 bytes of `context`; each `content_sha256` does the same for that
|
|
383
382
|
source's `content`. `sources` contains only file bodies that actually enter the final context, in
|
|
384
383
|
assembly order, after the existing newline normalization and truncation rules. `scope` distinguishes
|
|
385
|
-
`repository` from `global`; version 0.
|
|
384
|
+
`repository` from `global`; version 1.0.0's fixed root-file selection currently emits only
|
|
386
385
|
`repository` sources and does not add any global-file discovery.
|
|
387
386
|
|
|
388
387
|
The optional `selection` object is present only on a rendered `AGENTS.md` source. Its section entries
|
|
@@ -390,7 +389,7 @@ contain heading, heading level, and fixed selection reasons; it never contains t
|
|
|
390
389
|
target path. Existing source fields and schema version 1 remain unchanged.
|
|
391
390
|
|
|
392
391
|
The JSON schema version and package version are independent: `schema_version` is currently the
|
|
393
|
-
integer `1`, while `tool.version` is `0.
|
|
392
|
+
integer `1`, while `tool.version` is `1.0.0`. Callers must depend only on fields declared above.
|
|
394
393
|
The document contains no generated time or random identifier, so unchanged input produces identical
|
|
395
394
|
JSON bytes. On failure, stdout remains empty and stderr contains only a short diagnostic.
|
|
396
395
|
|
|
@@ -447,11 +446,15 @@ scan that ends before EOF still reports the characters it could not select.
|
|
|
447
446
|
- Directory tree: 12 KiB, 300 entries, and depth 2
|
|
448
447
|
- Working-tree changes: 100 paths and 4 KiB
|
|
449
448
|
- Recent commits: 8
|
|
449
|
+
- Git subprocess output: 16 MiB (bounded while reading)
|
|
450
|
+
- Candidate file validation: 16 MiB hard read bound
|
|
450
451
|
|
|
451
452
|
Truncation occurs only at complete UTF-8 and line boundaries and is marked explicitly.
|
|
452
|
-
These are output/capture limits. Eligible regular files are
|
|
453
|
-
and reject NUL bytes, including beyond the captured prefix
|
|
454
|
-
|
|
453
|
+
These are output/capture limits. Eligible regular files are streamed up to a hard safety limit of
|
|
454
|
+
16 MiB to validate UTF-8 and reject NUL bytes, including beyond the captured prefix. A file exceeding
|
|
455
|
+
this limit or failing validation fails closed and is skipped (`Skipped: unreadable.` /
|
|
456
|
+
`Skipped: unsupported text encoding.`); pathological files cannot require unbounded scanning.
|
|
457
|
+
Symlinked candidate files are skipped, and directory symlinks are not traversed.
|
|
455
458
|
|
|
456
459
|
## Git State Semantics
|
|
457
460
|
|
|
@@ -479,7 +482,7 @@ or candidate-file content.
|
|
|
479
482
|
|
|
480
483
|
## Not Included
|
|
481
484
|
|
|
482
|
-
Version 0.
|
|
485
|
+
Version 1.0.0 does not provide AI summaries, project-type detection, nested `AGENTS.md` handling,
|
|
483
486
|
Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
|
|
484
487
|
network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
|
|
485
488
|
|
|
@@ -20,16 +20,16 @@ pip install context-loader
|
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
|
|
23
|
+
project-context --repo /path/to/repo
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
|
|
27
|
+
project-context --repo /path/to/repo --format json
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
## Install
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Source version: `1.0.0`. Install its matching published release or an exact source commit.
|
|
33
33
|
|
|
34
34
|
Install via `uv`:
|
|
35
35
|
|
|
@@ -43,25 +43,24 @@ Install via `pip`:
|
|
|
43
43
|
pip install context-loader
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
For development or source-based installs tracking current repository (`0.
|
|
46
|
+
For development or source-based installs tracking current repository (`1.0.0`):
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
49
|
uv tool install git+https://github.com/xuanheng-tech/context-loader.git
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
The repository also retains `./
|
|
52
|
+
The repository also retains `./project-context` as a direct development entry point.
|
|
53
53
|
|
|
54
54
|
### Distributions
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
The sole distribution is **`context-loader`**, containing the `context_loader` runtime
|
|
57
|
+
and the `project-context` console script. Version `1.0.0` breaks the CLI name and Markdown
|
|
58
|
+
heading contract. The JSON schema is unchanged. Terminal and any executor call this same
|
|
59
|
+
entry point with explicit repository and focus arguments; no provider adapter or private
|
|
60
|
+
session state is involved.
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
so installing either name yields exactly one implementation and one CLI. Existing pins keep
|
|
63
|
-
working, and releases `0.1.5`-`0.1.7` of the old name are unchanged. New integrations should
|
|
64
|
-
depend on `context-loader`.
|
|
62
|
+
Previously published packages and historical release records remain unchanged. The new
|
|
63
|
+
release publishes no compatibility distribution or legacy executable alias.
|
|
65
64
|
|
|
66
65
|
The wheel installs the runtime package only. The source distribution additionally carries
|
|
67
66
|
`tool_cli_contract.json`, so a package-only consumer can pin the declared public CLI contract
|
|
@@ -91,11 +90,11 @@ automatic redaction**. Inspect the output before sharing it with a person or ext
|
|
|
91
90
|
## Usage
|
|
92
91
|
|
|
93
92
|
```bash
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
project-context --version
|
|
94
|
+
project-context --repo /home/user/projects/example
|
|
95
|
+
project-context --repo /home/user/projects/example --format markdown
|
|
96
|
+
project-context --repo /home/user/projects/example --format json
|
|
97
|
+
project-context --repo /home/user/projects/example \
|
|
99
98
|
--focus "Authentication and session management" \
|
|
100
99
|
--path auth/session.py
|
|
101
100
|
```
|
|
@@ -135,7 +134,7 @@ Keys are serialized in sorted order with `ensure_ascii=False`. The declared cont
|
|
|
135
134
|
"schema_version": 1,
|
|
136
135
|
"tool": {
|
|
137
136
|
"name": "context-loader",
|
|
138
|
-
"version": "0.
|
|
137
|
+
"version": "1.0.0"
|
|
139
138
|
},
|
|
140
139
|
"repository": {
|
|
141
140
|
"requested_path": "/canonical/requested/path",
|
|
@@ -171,7 +170,7 @@ Keys are serialized in sorted order with `ensure_ascii=False`. The declared cont
|
|
|
171
170
|
`context_sha256` hashes the UTF-8 bytes of `context`; each `content_sha256` does the same for that
|
|
172
171
|
source's `content`. `sources` contains only file bodies that actually enter the final context, in
|
|
173
172
|
assembly order, after the existing newline normalization and truncation rules. `scope` distinguishes
|
|
174
|
-
`repository` from `global`; version 0.
|
|
173
|
+
`repository` from `global`; version 1.0.0's fixed root-file selection currently emits only
|
|
175
174
|
`repository` sources and does not add any global-file discovery.
|
|
176
175
|
|
|
177
176
|
The optional `selection` object is present only on a rendered `AGENTS.md` source. Its section entries
|
|
@@ -179,7 +178,7 @@ contain heading, heading level, and fixed selection reasons; it never contains t
|
|
|
179
178
|
target path. Existing source fields and schema version 1 remain unchanged.
|
|
180
179
|
|
|
181
180
|
The JSON schema version and package version are independent: `schema_version` is currently the
|
|
182
|
-
integer `1`, while `tool.version` is `0.
|
|
181
|
+
integer `1`, while `tool.version` is `1.0.0`. Callers must depend only on fields declared above.
|
|
183
182
|
The document contains no generated time or random identifier, so unchanged input produces identical
|
|
184
183
|
JSON bytes. On failure, stdout remains empty and stderr contains only a short diagnostic.
|
|
185
184
|
|
|
@@ -236,11 +235,15 @@ scan that ends before EOF still reports the characters it could not select.
|
|
|
236
235
|
- Directory tree: 12 KiB, 300 entries, and depth 2
|
|
237
236
|
- Working-tree changes: 100 paths and 4 KiB
|
|
238
237
|
- Recent commits: 8
|
|
238
|
+
- Git subprocess output: 16 MiB (bounded while reading)
|
|
239
|
+
- Candidate file validation: 16 MiB hard read bound
|
|
239
240
|
|
|
240
241
|
Truncation occurs only at complete UTF-8 and line boundaries and is marked explicitly.
|
|
241
|
-
These are output/capture limits. Eligible regular files are
|
|
242
|
-
and reject NUL bytes, including beyond the captured prefix
|
|
243
|
-
|
|
242
|
+
These are output/capture limits. Eligible regular files are streamed up to a hard safety limit of
|
|
243
|
+
16 MiB to validate UTF-8 and reject NUL bytes, including beyond the captured prefix. A file exceeding
|
|
244
|
+
this limit or failing validation fails closed and is skipped (`Skipped: unreadable.` /
|
|
245
|
+
`Skipped: unsupported text encoding.`); pathological files cannot require unbounded scanning.
|
|
246
|
+
Symlinked candidate files are skipped, and directory symlinks are not traversed.
|
|
244
247
|
|
|
245
248
|
## Git State Semantics
|
|
246
249
|
|
|
@@ -268,7 +271,7 @@ or candidate-file content.
|
|
|
268
271
|
|
|
269
272
|
## Not Included
|
|
270
273
|
|
|
271
|
-
Version 0.
|
|
274
|
+
Version 1.0.0 does not provide AI summaries, project-type detection, nested `AGENTS.md` handling,
|
|
272
275
|
Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
|
|
273
276
|
network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
|
|
274
277
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Command-line interface for deterministic
|
|
1
|
+
"""Command-line interface for deterministic project context."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -19,7 +19,7 @@ class SafeArgumentParser(argparse.ArgumentParser):
|
|
|
19
19
|
|
|
20
20
|
def _parser() -> argparse.ArgumentParser:
|
|
21
21
|
parser = SafeArgumentParser(
|
|
22
|
-
prog="
|
|
22
|
+
prog="project-context",
|
|
23
23
|
description="Render deterministic local Git context as Markdown or JSON.",
|
|
24
24
|
allow_abbrev=False,
|
|
25
25
|
)
|
|
@@ -26,6 +26,8 @@ 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
|
+
FILE_SCAN_LIMIT_BYTES = 16 * 1024 * 1024
|
|
30
|
+
MAX_FILE_SCAN_BYTES = FILE_SCAN_LIMIT_BYTES
|
|
29
31
|
TRUNCATION_MARKER = "… truncated by context-loader …"
|
|
30
32
|
|
|
31
33
|
NOT_PRESENT = "Not present."
|
|
@@ -705,11 +707,15 @@ def _read_validated_text(file_descriptor: int, limit: int) -> tuple[str, bool, s
|
|
|
705
707
|
source_characters += 1
|
|
706
708
|
append(character)
|
|
707
709
|
|
|
710
|
+
total_bytes_read = 0
|
|
708
711
|
try:
|
|
709
712
|
while True:
|
|
710
713
|
raw = os.read(file_descriptor, 64 * 1024)
|
|
711
714
|
if not raw:
|
|
712
715
|
break
|
|
716
|
+
total_bytes_read += len(raw)
|
|
717
|
+
if total_bytes_read > FILE_SCAN_LIMIT_BYTES:
|
|
718
|
+
return "", False, SKIPPED_UNREADABLE, 0
|
|
713
719
|
if b"\0" in raw:
|
|
714
720
|
return "", False, SKIPPED_ENCODING, 0
|
|
715
721
|
consume(decoder.decode(raw, final=False))
|
|
@@ -743,6 +749,8 @@ def _collect_root_file(root: Path, name: str, language: str, limit: int) -> Coll
|
|
|
743
749
|
return CollectedFile(name, language, SKIPPED_SYMLINK)
|
|
744
750
|
if not stat.S_ISREG(metadata.st_mode):
|
|
745
751
|
return CollectedFile(name, language, SKIPPED_NOT_REGULAR)
|
|
752
|
+
if metadata.st_size > FILE_SCAN_LIMIT_BYTES:
|
|
753
|
+
return CollectedFile(name, language, SKIPPED_UNREADABLE)
|
|
746
754
|
|
|
747
755
|
flags = os.O_RDONLY | os.O_CLOEXEC | os.O_NONBLOCK | getattr(os, "O_NOFOLLOW", 0)
|
|
748
756
|
try:
|
|
@@ -752,8 +760,11 @@ def _collect_root_file(root: Path, name: str, language: str, limit: int) -> Coll
|
|
|
752
760
|
return CollectedFile(name, language, SKIPPED_SYMLINK)
|
|
753
761
|
return CollectedFile(name, language, SKIPPED_UNREADABLE)
|
|
754
762
|
try:
|
|
755
|
-
|
|
763
|
+
stat_result = os.fstat(file_descriptor)
|
|
764
|
+
if not stat.S_ISREG(stat_result.st_mode):
|
|
756
765
|
return CollectedFile(name, language, SKIPPED_NOT_REGULAR)
|
|
766
|
+
if stat_result.st_size > FILE_SCAN_LIMIT_BYTES:
|
|
767
|
+
return CollectedFile(name, language, SKIPPED_UNREADABLE)
|
|
757
768
|
content, truncated, error_status, source_characters = _read_validated_text(
|
|
758
769
|
file_descriptor, limit
|
|
759
770
|
)
|
|
@@ -4,7 +4,9 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import re
|
|
7
|
+
import selectors
|
|
7
8
|
import subprocess
|
|
9
|
+
import time
|
|
8
10
|
from dataclasses import dataclass
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
from types import MappingProxyType
|
|
@@ -108,25 +110,81 @@ class RepositoryLocation:
|
|
|
108
110
|
canonical_root: Path
|
|
109
111
|
|
|
110
112
|
|
|
113
|
+
def _read_bounded_process_stdout(
|
|
114
|
+
proc: subprocess.Popen[bytes],
|
|
115
|
+
max_bytes: int,
|
|
116
|
+
timeout_seconds: float,
|
|
117
|
+
) -> bytes:
|
|
118
|
+
deadline = time.monotonic() + timeout_seconds
|
|
119
|
+
buffer = bytearray()
|
|
120
|
+
assert proc.stdout is not None
|
|
121
|
+
os.set_blocking(proc.stdout.fileno(), False)
|
|
122
|
+
selector = selectors.DefaultSelector()
|
|
123
|
+
selector.register(proc.stdout, selectors.EVENT_READ)
|
|
124
|
+
try:
|
|
125
|
+
while True:
|
|
126
|
+
remaining = deadline - time.monotonic()
|
|
127
|
+
if remaining <= 0:
|
|
128
|
+
proc.kill()
|
|
129
|
+
proc.wait()
|
|
130
|
+
raise subprocess.TimeoutExpired(proc.args, timeout_seconds)
|
|
131
|
+
events = selector.select(timeout=max(0.0, remaining))
|
|
132
|
+
if not events:
|
|
133
|
+
proc.kill()
|
|
134
|
+
proc.wait()
|
|
135
|
+
raise subprocess.TimeoutExpired(proc.args, timeout_seconds)
|
|
136
|
+
try:
|
|
137
|
+
chunk = os.read(proc.stdout.fileno(), 64 * 1024)
|
|
138
|
+
except BlockingIOError:
|
|
139
|
+
continue
|
|
140
|
+
if not chunk:
|
|
141
|
+
break
|
|
142
|
+
buffer.extend(chunk)
|
|
143
|
+
if len(buffer) > max_bytes:
|
|
144
|
+
proc.kill()
|
|
145
|
+
proc.wait()
|
|
146
|
+
raise ContextLoaderError("repository Git output exceeded the safety limit")
|
|
147
|
+
except Exception:
|
|
148
|
+
try:
|
|
149
|
+
proc.kill()
|
|
150
|
+
proc.wait()
|
|
151
|
+
except OSError:
|
|
152
|
+
pass
|
|
153
|
+
raise
|
|
154
|
+
finally:
|
|
155
|
+
selector.close()
|
|
156
|
+
|
|
157
|
+
remaining = max(0.0, deadline - time.monotonic())
|
|
158
|
+
try:
|
|
159
|
+
proc.wait(timeout=remaining)
|
|
160
|
+
except subprocess.TimeoutExpired:
|
|
161
|
+
proc.kill()
|
|
162
|
+
proc.wait()
|
|
163
|
+
raise
|
|
164
|
+
return bytes(buffer)
|
|
165
|
+
|
|
166
|
+
|
|
111
167
|
def _run_git(repo: Path, arguments: tuple[str, ...], *, check: bool = True) -> GitResult:
|
|
112
168
|
argv = [GIT_EXECUTABLE, *_GIT_PREFIX, "-C", os.fspath(repo), *arguments]
|
|
113
169
|
try:
|
|
114
|
-
|
|
170
|
+
with subprocess.Popen( # noqa: S603 - executable and every command are fixed here.
|
|
115
171
|
argv,
|
|
116
172
|
cwd="/",
|
|
117
173
|
env=dict(_GIT_ENVIRONMENT),
|
|
118
174
|
stdin=subprocess.DEVNULL,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
175
|
+
stdout=subprocess.PIPE,
|
|
176
|
+
stderr=subprocess.DEVNULL,
|
|
177
|
+
) as proc:
|
|
178
|
+
stdout = _read_bounded_process_stdout(proc, MAX_GIT_OUTPUT_BYTES, GIT_TIMEOUT_SECONDS)
|
|
179
|
+
returncode = proc.returncode
|
|
180
|
+
except ContextLoaderError:
|
|
181
|
+
raise
|
|
123
182
|
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
124
183
|
raise ContextLoaderError("unable to read repository Git state") from exc
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if check and completed.returncode != 0:
|
|
184
|
+
|
|
185
|
+
if check and returncode != 0:
|
|
128
186
|
raise ContextLoaderError("unable to read repository Git state")
|
|
129
|
-
return GitResult(
|
|
187
|
+
return GitResult(stdout, returncode)
|
|
130
188
|
|
|
131
189
|
|
|
132
190
|
def _without_one_line_ending(raw: bytes) -> bytes:
|
|
@@ -292,7 +292,7 @@ def render_markdown_with_details(state: RepositoryState, project: ProjectContext
|
|
|
292
292
|
"""Render all sections in fixed order without exceeding the global byte limit."""
|
|
293
293
|
header = "\n".join(
|
|
294
294
|
(
|
|
295
|
-
"#
|
|
295
|
+
"# Project Context",
|
|
296
296
|
"",
|
|
297
297
|
f"- Schema: `{SCHEMA}`",
|
|
298
298
|
f"- Repository: `{_display(os.fspath(state.repository))}`",
|
|
@@ -4,8 +4,8 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "context-loader"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Render deterministic local Git context for
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Render deterministic local Git context for any executor"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12,<3.13"
|
|
11
11
|
dependencies = []
|
|
@@ -14,7 +14,7 @@ dependencies = []
|
|
|
14
14
|
file = "LICENSE"
|
|
15
15
|
|
|
16
16
|
[project.scripts]
|
|
17
|
-
|
|
17
|
+
project-context = "context_loader.cli:main"
|
|
18
18
|
|
|
19
19
|
[project.urls]
|
|
20
20
|
Repository = "https://github.com/xuanheng-tech/context-loader"
|
|
@@ -4,15 +4,15 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "context-loader"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Render deterministic local Git context for
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Render deterministic local Git context for any executor"
|
|
9
9
|
license = { file = "LICENSE" }
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.12,<3.13"
|
|
12
12
|
dependencies = []
|
|
13
13
|
|
|
14
14
|
[project.scripts]
|
|
15
|
-
|
|
15
|
+
project-context = "context_loader.cli:main"
|
|
16
16
|
|
|
17
17
|
[project.urls]
|
|
18
18
|
Repository = "https://github.com/xuanheng-tech/context-loader"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"--version",
|
|
10
10
|
"--help"
|
|
11
11
|
],
|
|
12
|
-
"name": "
|
|
12
|
+
"name": "project-context",
|
|
13
13
|
"operation_class": "read_only",
|
|
14
14
|
"preconditions": [
|
|
15
15
|
"repo_is_absolute_git_worktree",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
]
|
|
25
25
|
}
|
|
26
26
|
],
|
|
27
|
-
"contract_version":
|
|
27
|
+
"contract_version": 2,
|
|
28
28
|
"flags": {
|
|
29
29
|
"format": [
|
|
30
30
|
"markdown",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
],
|
|
45
45
|
"schema_version": 1,
|
|
46
46
|
"tool_name": "context-loader",
|
|
47
|
-
"tool_version": "0.
|
|
47
|
+
"tool_version": "1.0.0"
|
|
48
48
|
}
|
|
File without changes
|
|
File without changes
|