context-loader 1.3.5__tar.gz → 1.3.7__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.5 → context_loader-1.3.7}/PKG-INFO +27 -12
- {context_loader-1.3.5 → context_loader-1.3.7}/README.md +26 -11
- {context_loader-1.3.5 → context_loader-1.3.7}/context_loader/__init__.py +1 -1
- {context_loader-1.3.5 → context_loader-1.3.7}/context_loader/collect.py +42 -13
- {context_loader-1.3.5 → context_loader-1.3.7}/pyproject.toml +1 -1
- {context_loader-1.3.5 → context_loader-1.3.7}/pyproject.toml.orig +1 -1
- {context_loader-1.3.5 → context_loader-1.3.7}/tool_cli_contract.json +1 -1
- {context_loader-1.3.5 → context_loader-1.3.7}/LICENSE +0 -0
- {context_loader-1.3.5 → context_loader-1.3.7}/context_loader/application.py +0 -0
- {context_loader-1.3.5 → context_loader-1.3.7}/context_loader/cli.py +0 -0
- {context_loader-1.3.5 → context_loader-1.3.7}/context_loader/filesystem.py +0 -0
- {context_loader-1.3.5 → context_loader-1.3.7}/context_loader/git.py +0 -0
- {context_loader-1.3.5 → context_loader-1.3.7}/context_loader/model.py +0 -0
- {context_loader-1.3.5 → context_loader-1.3.7}/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.
|
|
3
|
+
Version: 1.3.7
|
|
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.7**. 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.7'
|
|
226
226
|
```
|
|
227
227
|
|
|
228
228
|
```bash
|
|
229
|
-
pip install 'context-loader==1.3.
|
|
229
|
+
pip install 'context-loader==1.3.7'
|
|
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.7`. 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.7'
|
|
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.7'
|
|
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.7`):
|
|
257
257
|
|
|
258
258
|
```bash
|
|
259
259
|
uv tool install git+https://github.com/xuanheng-tech/context-loader.git
|
|
@@ -350,7 +350,7 @@ set differs, as described under “Compact model consumption”. The declared co
|
|
|
350
350
|
"schema_version": 3,
|
|
351
351
|
"tool": {
|
|
352
352
|
"name": "context-loader",
|
|
353
|
-
"version": "1.3.
|
|
353
|
+
"version": "1.3.7"
|
|
354
354
|
},
|
|
355
355
|
"repository": {
|
|
356
356
|
"requested_path": "/canonical/requested/path",
|
|
@@ -470,7 +470,7 @@ although 1.1.0 added the top-level `statuses` key. From release 1.2.0 onward eac
|
|
|
470
470
|
names exactly one key set, and a new number is issued whenever a key set would otherwise be reused.
|
|
471
471
|
|
|
472
472
|
The JSON schema version and package version are independent: this build emits `schema_version` `3`
|
|
473
|
-
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.7`. Version 1
|
|
474
474
|
and 2 are the exact shapes already published in release 1.2.0 and are never reused: because
|
|
475
475
|
`nested_context` changes the default document's key set, version 3 names the current full-document
|
|
476
476
|
key set and version 4 names the compact projection.
|
|
@@ -512,12 +512,27 @@ heading on the first line is still recognized as a heading.
|
|
|
512
512
|
|
|
513
513
|
The root `AGENTS.md` is split at Markdown headings outside fenced code blocks. The output always
|
|
514
514
|
starts with complete early sections fitting a 4-KiB head, then adds complete relevant sections in
|
|
515
|
-
source order using
|
|
515
|
+
source order using normalized focus/path tokens and their parent context. Remaining headings
|
|
516
516
|
appear in an explicit index whose body text is not loaded. With no selection signals, only the small
|
|
517
517
|
head and index are emitted. Unsafe heading parsing falls back to a bounded head and an explicit
|
|
518
518
|
manual-recovery notice. A head whose own headings would not fit the budget falls back to the same
|
|
519
519
|
bounded head without a section index; a single instruction file never fails the whole collection.
|
|
520
520
|
|
|
521
|
+
Matching uses NFKC normalization and case folding. Ordinary tokens require at least three characters;
|
|
522
|
+
two-character Han terms such as `安全` and `部署`, and the technical abbreviations `AI`, `CD`, `CI`,
|
|
523
|
+
`DB`, `IO`, `JS`, `ML`, `OS`, `PR`, `QA`, `TS`, `UI` and `UX`, are also eligible. Han terms can match
|
|
524
|
+
within entirely Han words, so `安全` matches `安全规则`; Latin terms still require complete tokens,
|
|
525
|
+
so `CI` does not match `special`. Existing stopwords, scoring and byte budgets still apply. Repeating
|
|
526
|
+
a Han term in a heading and body does not increase its score. This is literal matching without word
|
|
527
|
+
segmentation, synonyms or translation.
|
|
528
|
+
|
|
529
|
+
Two distinct eligible focus terms in the same section body can select that section even when its
|
|
530
|
+
heading does not contain them; a single incidental body term is not enough on its own. Heading
|
|
531
|
+
terms keep their stronger weighting. Generic directory components such as `packages`,
|
|
532
|
+
`data` and `research` are ignored as path terms; exact path occurrences and specific components
|
|
533
|
+
remain eligible. For long instructions, use terms from the actual headings or rule text and inspect
|
|
534
|
+
the omitted-heading index before assuming a rule is absent.
|
|
535
|
+
|
|
521
536
|
`Omitted source characters` is measured against the whole normalized source, so a bounded source
|
|
522
537
|
scan that ends before EOF still reports the characters it could not select.
|
|
523
538
|
|
|
@@ -647,7 +662,7 @@ or candidate-file content.
|
|
|
647
662
|
|
|
648
663
|
## Not Included
|
|
649
664
|
|
|
650
|
-
Version 1.3.
|
|
665
|
+
Version 1.3.7 does not provide AI summaries, project-type detection, loading or transport of
|
|
651
666
|
nested `AGENTS.md` contents (the JSON `nested_context` field reports bounded existence only),
|
|
652
667
|
Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
|
|
653
668
|
network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
|
|
@@ -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.7**. 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.7'
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
pip install 'context-loader==1.3.
|
|
18
|
+
pip install 'context-loader==1.3.7'
|
|
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.7`. 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.7'
|
|
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.7'
|
|
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.7`):
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
uv tool install git+https://github.com/xuanheng-tech/context-loader.git
|
|
@@ -139,7 +139,7 @@ set differs, as described under “Compact model consumption”. The declared co
|
|
|
139
139
|
"schema_version": 3,
|
|
140
140
|
"tool": {
|
|
141
141
|
"name": "context-loader",
|
|
142
|
-
"version": "1.3.
|
|
142
|
+
"version": "1.3.7"
|
|
143
143
|
},
|
|
144
144
|
"repository": {
|
|
145
145
|
"requested_path": "/canonical/requested/path",
|
|
@@ -259,7 +259,7 @@ although 1.1.0 added the top-level `statuses` key. From release 1.2.0 onward eac
|
|
|
259
259
|
names exactly one key set, and a new number is issued whenever a key set would otherwise be reused.
|
|
260
260
|
|
|
261
261
|
The JSON schema version and package version are independent: this build emits `schema_version` `3`
|
|
262
|
-
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.7`. Version 1
|
|
263
263
|
and 2 are the exact shapes already published in release 1.2.0 and are never reused: because
|
|
264
264
|
`nested_context` changes the default document's key set, version 3 names the current full-document
|
|
265
265
|
key set and version 4 names the compact projection.
|
|
@@ -301,12 +301,27 @@ heading on the first line is still recognized as a heading.
|
|
|
301
301
|
|
|
302
302
|
The root `AGENTS.md` is split at Markdown headings outside fenced code blocks. The output always
|
|
303
303
|
starts with complete early sections fitting a 4-KiB head, then adds complete relevant sections in
|
|
304
|
-
source order using
|
|
304
|
+
source order using normalized focus/path tokens and their parent context. Remaining headings
|
|
305
305
|
appear in an explicit index whose body text is not loaded. With no selection signals, only the small
|
|
306
306
|
head and index are emitted. Unsafe heading parsing falls back to a bounded head and an explicit
|
|
307
307
|
manual-recovery notice. A head whose own headings would not fit the budget falls back to the same
|
|
308
308
|
bounded head without a section index; a single instruction file never fails the whole collection.
|
|
309
309
|
|
|
310
|
+
Matching uses NFKC normalization and case folding. Ordinary tokens require at least three characters;
|
|
311
|
+
two-character Han terms such as `安全` and `部署`, and the technical abbreviations `AI`, `CD`, `CI`,
|
|
312
|
+
`DB`, `IO`, `JS`, `ML`, `OS`, `PR`, `QA`, `TS`, `UI` and `UX`, are also eligible. Han terms can match
|
|
313
|
+
within entirely Han words, so `安全` matches `安全规则`; Latin terms still require complete tokens,
|
|
314
|
+
so `CI` does not match `special`. Existing stopwords, scoring and byte budgets still apply. Repeating
|
|
315
|
+
a Han term in a heading and body does not increase its score. This is literal matching without word
|
|
316
|
+
segmentation, synonyms or translation.
|
|
317
|
+
|
|
318
|
+
Two distinct eligible focus terms in the same section body can select that section even when its
|
|
319
|
+
heading does not contain them; a single incidental body term is not enough on its own. Heading
|
|
320
|
+
terms keep their stronger weighting. Generic directory components such as `packages`,
|
|
321
|
+
`data` and `research` are ignored as path terms; exact path occurrences and specific components
|
|
322
|
+
remain eligible. For long instructions, use terms from the actual headings or rule text and inspect
|
|
323
|
+
the omitted-heading index before assuming a rule is absent.
|
|
324
|
+
|
|
310
325
|
`Omitted source characters` is measured against the whole normalized source, so a bounded source
|
|
311
326
|
scan that ends before EOF still reports the characters it could not select.
|
|
312
327
|
|
|
@@ -436,7 +451,7 @@ or candidate-file content.
|
|
|
436
451
|
|
|
437
452
|
## Not Included
|
|
438
453
|
|
|
439
|
-
Version 1.3.
|
|
454
|
+
Version 1.3.7 does not provide AI summaries, project-type detection, loading or transport of
|
|
440
455
|
nested `AGENTS.md` contents (the JSON `nested_context` field reports bounded existence only),
|
|
441
456
|
Memory retrieval, semantic ranking, ignore-rule parsing, plugins, profiles, caches, databases,
|
|
442
457
|
network services, MCP, daemons, GUIs, CI/CD, telemetry, or automatic updates.
|
|
@@ -68,6 +68,9 @@ _MAKE_TARGET_RE = re.compile(
|
|
|
68
68
|
)
|
|
69
69
|
_ATX_HEADING_RE = re.compile(r"^ {0,3}(#{1,6})(?:[ \t]+(.*)|[ \t]*)$")
|
|
70
70
|
_TOKEN_RE = re.compile(r"[^\W_]+", re.UNICODE)
|
|
71
|
+
_SHORT_TECHNICAL_TOKENS = frozenset(
|
|
72
|
+
{"ai", "cd", "ci", "db", "io", "js", "ml", "os", "pr", "qa", "ts", "ui", "ux"}
|
|
73
|
+
)
|
|
71
74
|
_MATCH_TOKEN_STOPWORDS = frozenset(
|
|
72
75
|
{
|
|
73
76
|
"and",
|
|
@@ -115,9 +118,11 @@ _PATH_TOKEN_STOPWORDS = _MATCH_TOKEN_STOPWORDS | {
|
|
|
115
118
|
"py",
|
|
116
119
|
"python",
|
|
117
120
|
"quant",
|
|
121
|
+
"research",
|
|
118
122
|
"scripts",
|
|
119
123
|
"src",
|
|
120
124
|
}
|
|
125
|
+
_MIN_SECTION_RELEVANCE = 4
|
|
121
126
|
_SELECTION_REASON_ORDER = (
|
|
122
127
|
"head",
|
|
123
128
|
"focus_match",
|
|
@@ -138,11 +143,35 @@ def _normalized_text(value: str) -> str:
|
|
|
138
143
|
return unicodedata.normalize("NFKC", value).casefold()
|
|
139
144
|
|
|
140
145
|
|
|
146
|
+
def _is_han_token(value: str) -> bool:
|
|
147
|
+
return bool(value) and all(
|
|
148
|
+
unicodedata.name(character, "").startswith("CJK UNIFIED IDEOGRAPH-") for character in value
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
141
152
|
def _matching_tokens(value: str, stopwords: frozenset[str]) -> frozenset[str]:
|
|
142
153
|
return frozenset(
|
|
143
154
|
token
|
|
144
155
|
for token in _TOKEN_RE.findall(_normalized_text(value))
|
|
145
|
-
if
|
|
156
|
+
if token not in stopwords
|
|
157
|
+
and (
|
|
158
|
+
len(token) >= 3
|
|
159
|
+
or token in _SHORT_TECHNICAL_TOKENS
|
|
160
|
+
or (len(token) == 2 and _is_han_token(token))
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _matched_terms(terms: frozenset[str], tokens: frozenset[str]) -> frozenset[str]:
|
|
166
|
+
matches = terms & tokens
|
|
167
|
+
han_terms = tuple(term for term in terms - matches if _is_han_token(term))
|
|
168
|
+
if not han_terms:
|
|
169
|
+
return matches
|
|
170
|
+
# Han text need not delimit words with spaces. Count each query term once,
|
|
171
|
+
# without introducing substring matching for Latin words or abbreviations.
|
|
172
|
+
han_tokens = tuple(token for token in tokens if _is_han_token(token))
|
|
173
|
+
return matches | frozenset(
|
|
174
|
+
term for term in han_terms if any(term in token for token in han_tokens)
|
|
146
175
|
)
|
|
147
176
|
|
|
148
177
|
|
|
@@ -334,24 +363,24 @@ def _small_head_end(content: str, sections: tuple[MarkdownSection, ...]) -> int:
|
|
|
334
363
|
def _section_relevance(
|
|
335
364
|
section: MarkdownSection, signals: _SelectionSignals
|
|
336
365
|
) -> tuple[int, frozenset[str]]:
|
|
337
|
-
heading_tokens =
|
|
338
|
-
token
|
|
339
|
-
for token in section.normalized_heading.split()
|
|
340
|
-
if len(token) >= 3 and token not in _MATCH_TOKEN_STOPWORDS
|
|
341
|
-
)
|
|
366
|
+
heading_tokens = _matching_tokens(section.normalized_heading, _MATCH_TOKEN_STOPWORDS)
|
|
342
367
|
section_tokens = _matching_tokens(section.text, _MATCH_TOKEN_STOPWORDS)
|
|
343
368
|
body_tokens = section_tokens - heading_tokens
|
|
344
369
|
score = 0
|
|
345
370
|
reasons: set[str] = set()
|
|
346
371
|
|
|
347
|
-
focus_heading = signals.focus_tokens
|
|
348
|
-
focus_body = signals.focus_tokens
|
|
372
|
+
focus_heading = _matched_terms(signals.focus_tokens, heading_tokens)
|
|
373
|
+
focus_body = _matched_terms(signals.focus_tokens, body_tokens) - focus_heading
|
|
349
374
|
if focus_heading or focus_body:
|
|
350
375
|
reasons.add("focus_match")
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
376
|
+
focus_score = 6 * len(focus_heading) + len(focus_body)
|
|
377
|
+
# Two distinct terms in one body suffice without outranking a heading match.
|
|
378
|
+
if len(focus_body) >= 2:
|
|
379
|
+
focus_score = max(_MIN_SECTION_RELEVANCE, focus_score)
|
|
380
|
+
score += focus_score
|
|
381
|
+
|
|
382
|
+
path_heading = _matched_terms(signals.path_tokens, heading_tokens)
|
|
383
|
+
path_body = _matched_terms(signals.path_tokens, body_tokens) - path_heading
|
|
355
384
|
exact_path = bool(
|
|
356
385
|
signals.normalized_path
|
|
357
386
|
and _normalized_text(signals.normalized_path) in _normalized_text(section.text)
|
|
@@ -537,7 +566,7 @@ def _select_agents_content(source: CollectedFile, signals: _SelectionSignals) ->
|
|
|
537
566
|
for index, section in enumerate(sections):
|
|
538
567
|
if signals.focus_tokens or signals.path_tokens or signals.normalized_path:
|
|
539
568
|
score, section_reasons = _section_relevance(section, signals)
|
|
540
|
-
if score >=
|
|
569
|
+
if score >= _MIN_SECTION_RELEVANCE:
|
|
541
570
|
ranked.append((score, index, section_reasons))
|
|
542
571
|
ranked.sort(key=lambda item: (-item[0], sections[item[1]].start))
|
|
543
572
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|