context-loader 1.1.0__tar.gz → 1.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: context-loader
3
- Version: 1.1.0
3
+ Version: 1.2.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
- Context Loader is a local CLI that exports repository context deterministically for coding-agent workflows,
223
- rendering stable Markdown or JSON output.
222
+ Current stable release: **1.2.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.2.0'
227
226
  ```
228
227
 
229
228
  ```bash
230
- pip install context-loader
229
+ pip install 'context-loader==1.2.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.1.0`. Install its matching published release or an exact source commit.
242
+ Source version: `1.2.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.2.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.2.0'
255
254
  ```
256
255
 
257
- For development or source-based installs tracking current repository (`1.1.0`):
256
+ For development or source-based installs tracking current repository (`1.2.0`):
258
257
 
259
258
  ```bash
260
259
  uv tool install git+https://github.com/xuanheng-tech/context-loader.git
@@ -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 JSON mode, an absolute existing
315
- directory inside the working tree is accepted; symlinks are normalized and the discovered root is
316
- reported as `canonical_root`. Relative paths, non-Git directories, regular files, and bare
317
- repositories are rejected in both modes.
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 to stdout.
341
- Keys are serialized in sorted order with `ensure_ascii=False`. The declared contract is:
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
347
  "schema_version": 1,
346
348
  "tool": {
347
349
  "name": "context-loader",
348
- "version": "1.1.0"
350
+ "version": "1.2.0"
349
351
  },
350
352
  "repository": {
351
353
  "requested_path": "/canonical/requested/path",
@@ -380,10 +382,12 @@ Keys are serialized in sorted order with `ensure_ascii=False`. The declared cont
380
382
  ```
381
383
 
382
384
  `context_sha256` hashes the UTF-8 bytes of `context`; each `content_sha256` does the same for that
383
- source's `content`. `sources` contains only file bodies that actually enter the final context, in
384
- assembly order, after the existing newline normalization and truncation rules. `scope` distinguishes
385
- `repository` from `global`; version 1.0.0's fixed root-file selection currently emits only
386
- `repository` sources and does not add any global-file discovery.
385
+ source's `content` body as rendered (after newline normalization, any `AGENTS.md` section selection
386
+ and any truncation marker) — never for the raw bytes of the file at `path`, which match only for
387
+ unselected, untruncated LF files. `sources` contains only file bodies that actually enter the final
388
+ context, in assembly order, after the existing newline normalization and truncation rules. `scope`
389
+ distinguishes `repository` from `global`; version 1.0.0's fixed root-file selection currently emits
390
+ only `repository` sources and does not add any global-file discovery.
387
391
 
388
392
  `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, and truncated working-tree or declared-command listings. Each entry has stable `code`, `subject_kind`, and `subject` fields. `sources`, `context`, and `schema_version` remain unchanged; callers can ignore `statuses` safely.
389
393
 
@@ -391,8 +395,26 @@ The optional `selection` object is present only on a rendered `AGENTS.md` source
391
395
  contain heading, heading level, and fixed selection reasons; it never contains the original focus or
392
396
  target path. Existing source fields and schema version 1 remain unchanged.
393
397
 
394
- The JSON schema version and package version are independent: `schema_version` is currently the
395
- integer `1`, while `tool.version` is `1.1.0`. Callers must depend only on fields declared above.
398
+ ### Compact model consumption (`--format json-compact`)
399
+
400
+ `--format json-compact` emits a `schema_version` 2 document: exactly the version-1 document with
401
+ `sources[*].content` omitted. Every other field — `context`, `context_sha256`, `statuses`,
402
+ `warnings`, `tool`, `repository`, and each source's `ordinal`, `kind`, `scope`, `path`,
403
+ `content_sha256` and optional `selection` — is identical to `--format json` for the same arguments.
404
+ The projection exists because the selected source bodies already occur verbatim inside `context`,
405
+ so carrying them again in `sources` duplicates a large share of the document bytes (measured up to
406
+ ~44% on real worktrees, scaling with how much of `context` those bodies occupy) for a model
407
+ consumer without adding information. Nothing is lost: each omitted body remains inside `context`,
408
+ `content_sha256` still fingerprints that rendered body, and `path` plus `canonical_root` locate the
409
+ underlying file for a bounded manual re-read (whose raw bytes may differ from the rendered body as
410
+ defined above). `schema_version` is an exact document selector, not an upgrade marker: version 2 is
411
+ a field-set projection of version 1, so consumers must branch on the value and must not apply
412
+ `>=`-superset reasoning. Default Markdown and `--format json` output bytes, exit codes and
413
+ determinism are unchanged.
414
+
415
+ The JSON schema version and package version are independent: `--format json` currently emits
416
+ `schema_version` `1` and `--format json-compact` emits `2`, while `tool.version` is `1.2.0`.
417
+ Callers must depend only on fields declared above.
396
418
  The document contains no generated time or random identifier, so unchanged input produces identical
397
419
  JSON bytes. On failure, stdout remains empty and stderr contains only a short diagnostic.
398
420
 
@@ -500,8 +522,11 @@ just check
500
522
  ## Version maintenance
501
523
 
502
524
  The versions in `pyproject.toml` and `context_loader/__init__.py`, the matching `CHANGELOG.md`
503
- section, and required tests must change in the same release-preparation batch. `CHANGELOG.md` is the
504
- authoritative version-change record. Merging to `master` is not a release; formal publication
525
+ section, required tests, and the README current-stable declaration plus `context-loader==X.Y.Z`
526
+ install pins must change in the same release-preparation batch. `CHANGELOG.md` is the
527
+ authoritative version-change record. `scripts/release.py` rejects README stable/install
528
+ pins that disagree with the intended package version; historical changelog entries are
529
+ not part of that check. Merging to `master` is not a release; formal publication
505
530
  still requires a separately created and pushed tag.
506
531
 
507
532
  ## Release closure and retries
@@ -8,15 +8,14 @@ Python standard library.
8
8
 
9
9
  ## Open-source quick start
10
10
 
11
- Context Loader is a local CLI that exports repository context deterministically for coding-agent workflows,
12
- rendering stable Markdown or JSON output.
11
+ Current stable release: **1.2.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.2.0'
16
15
  ```
17
16
 
18
17
  ```bash
19
- pip install context-loader
18
+ pip install 'context-loader==1.2.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.1.0`. Install its matching published release or an exact source commit.
31
+ Source version: `1.2.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.2.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.2.0'
44
43
  ```
45
44
 
46
- For development or source-based installs tracking current repository (`1.1.0`):
45
+ For development or source-based installs tracking current repository (`1.2.0`):
47
46
 
48
47
  ```bash
49
48
  uv tool install git+https://github.com/xuanheng-tech/context-loader.git
@@ -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 JSON mode, an absolute existing
104
- directory inside the working tree is accepted; symlinks are normalized and the discovered root is
105
- reported as `canonical_root`. Relative paths, non-Git directories, regular files, and bare
106
- repositories are rejected in both modes.
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 to stdout.
130
- Keys are serialized in sorted order with `ensure_ascii=False`. The declared contract is:
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
136
  "schema_version": 1,
135
137
  "tool": {
136
138
  "name": "context-loader",
137
- "version": "1.1.0"
139
+ "version": "1.2.0"
138
140
  },
139
141
  "repository": {
140
142
  "requested_path": "/canonical/requested/path",
@@ -169,10 +171,12 @@ Keys are serialized in sorted order with `ensure_ascii=False`. The declared cont
169
171
  ```
170
172
 
171
173
  `context_sha256` hashes the UTF-8 bytes of `context`; each `content_sha256` does the same for that
172
- source's `content`. `sources` contains only file bodies that actually enter the final context, in
173
- assembly order, after the existing newline normalization and truncation rules. `scope` distinguishes
174
- `repository` from `global`; version 1.0.0's fixed root-file selection currently emits only
175
- `repository` sources and does not add any global-file discovery.
174
+ source's `content` body as rendered (after newline normalization, any `AGENTS.md` section selection
175
+ and any truncation marker) — never for the raw bytes of the file at `path`, which match only for
176
+ unselected, untruncated LF files. `sources` contains only file bodies that actually enter the final
177
+ context, in assembly order, after the existing newline normalization and truncation rules. `scope`
178
+ distinguishes `repository` from `global`; version 1.0.0's fixed root-file selection currently emits
179
+ only `repository` sources and does not add any global-file discovery.
176
180
 
177
181
  `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, and truncated working-tree or declared-command listings. Each entry has stable `code`, `subject_kind`, and `subject` fields. `sources`, `context`, and `schema_version` remain unchanged; callers can ignore `statuses` safely.
178
182
 
@@ -180,8 +184,26 @@ The optional `selection` object is present only on a rendered `AGENTS.md` source
180
184
  contain heading, heading level, and fixed selection reasons; it never contains the original focus or
181
185
  target path. Existing source fields and schema version 1 remain unchanged.
182
186
 
183
- The JSON schema version and package version are independent: `schema_version` is currently the
184
- integer `1`, while `tool.version` is `1.1.0`. Callers must depend only on fields declared above.
187
+ ### Compact model consumption (`--format json-compact`)
188
+
189
+ `--format json-compact` emits a `schema_version` 2 document: exactly the version-1 document with
190
+ `sources[*].content` omitted. Every other field — `context`, `context_sha256`, `statuses`,
191
+ `warnings`, `tool`, `repository`, and each source's `ordinal`, `kind`, `scope`, `path`,
192
+ `content_sha256` and optional `selection` — is identical to `--format json` for the same arguments.
193
+ The projection exists because the selected source bodies already occur verbatim inside `context`,
194
+ so carrying them again in `sources` duplicates a large share of the document bytes (measured up to
195
+ ~44% on real worktrees, scaling with how much of `context` those bodies occupy) for a model
196
+ consumer without adding information. Nothing is lost: each omitted body remains inside `context`,
197
+ `content_sha256` still fingerprints that rendered body, and `path` plus `canonical_root` locate the
198
+ underlying file for a bounded manual re-read (whose raw bytes may differ from the rendered body as
199
+ defined above). `schema_version` is an exact document selector, not an upgrade marker: version 2 is
200
+ a field-set projection of version 1, so consumers must branch on the value and must not apply
201
+ `>=`-superset reasoning. Default Markdown and `--format json` output bytes, exit codes and
202
+ determinism are unchanged.
203
+
204
+ The JSON schema version and package version are independent: `--format json` currently emits
205
+ `schema_version` `1` and `--format json-compact` emits `2`, while `tool.version` is `1.2.0`.
206
+ Callers must depend only on fields declared above.
185
207
  The document contains no generated time or random identifier, so unchanged input produces identical
186
208
  JSON bytes. On failure, stdout remains empty and stderr contains only a short diagnostic.
187
209
 
@@ -289,8 +311,11 @@ just check
289
311
  ## Version maintenance
290
312
 
291
313
  The versions in `pyproject.toml` and `context_loader/__init__.py`, the matching `CHANGELOG.md`
292
- section, and required tests must change in the same release-preparation batch. `CHANGELOG.md` is the
293
- authoritative version-change record. Merging to `master` is not a release; formal publication
314
+ section, required tests, and the README current-stable declaration plus `context-loader==X.Y.Z`
315
+ install pins must change in the same release-preparation batch. `CHANGELOG.md` is the
316
+ authoritative version-change record. `scripts/release.py` rejects README stable/install
317
+ pins that disagree with the intended package version; historical changelog entries are
318
+ not part of that check. Merging to `master` is not a release; formal publication
294
319
  still requires a separately created and pushed tag.
295
320
 
296
321
  ## Release closure and retries
@@ -1,3 +1,3 @@
1
1
  """Project Context Loader."""
2
2
 
3
- __version__ = "1.1.0"
3
+ __version__ = "1.2.0"
@@ -27,6 +27,7 @@ from .git import ContextLoaderError, collect_repository
27
27
  from .render import render_markdown_with_details, rendered_source_contents
28
28
 
29
29
  JSON_SCHEMA_VERSION = 1
30
+ COMPACT_JSON_SCHEMA_VERSION = 2
30
31
  TOOL_NAME = "context-loader"
31
32
 
32
33
  _STATUS_CODE_BY_MESSAGE = {
@@ -272,24 +273,30 @@ def _status_document(status: ContextStatus) -> dict[str, object]:
272
273
  }
273
274
 
274
275
 
275
- def _source_document(source: ProjectContextSource) -> dict[str, object]:
276
+ def _source_document(source: ProjectContextSource, *, include_content: bool) -> dict[str, object]:
276
277
  document: dict[str, object] = {
277
278
  "ordinal": source.ordinal,
278
279
  "kind": source.kind,
279
280
  "scope": source.scope,
280
281
  "path": os.fspath(source.path),
281
282
  "content_sha256": source.content_sha256,
282
- "content": source.content,
283
283
  }
284
+ if include_content:
285
+ document["content"] = source.content
284
286
  if source.selection is not None:
285
287
  document["selection"] = _selection_document(source.selection)
286
288
  return document
287
289
 
288
290
 
289
- def render_json(result: ProjectContextResult) -> bytes:
290
- """Serialize one result as stable UTF-8 JSON followed by exactly one newline."""
291
+ def render_json(result: ProjectContextResult, *, compact: bool = False) -> bytes:
292
+ """Serialize one result as stable UTF-8 JSON followed by exactly one newline.
293
+
294
+ ``compact`` projects away the duplicated source bodies only at this
295
+ boundary: statuses and context were computed from the full model at load
296
+ time, so both documents agree on everything except ``sources[*].content``.
297
+ """
291
298
  document = {
292
- "schema_version": result.schema_version,
299
+ "schema_version": (COMPACT_JSON_SCHEMA_VERSION if compact else result.schema_version),
293
300
  "tool": {
294
301
  "name": result.tool.name,
295
302
  "version": result.tool.version,
@@ -298,7 +305,9 @@ def render_json(result: ProjectContextResult) -> bytes:
298
305
  "requested_path": os.fspath(result.repository.requested_path),
299
306
  "canonical_root": os.fspath(result.repository.canonical_root),
300
307
  },
301
- "sources": [_source_document(source) for source in result.sources],
308
+ "sources": [
309
+ _source_document(source, include_content=not compact) for source in result.sources
310
+ ],
302
311
  "statuses": [_status_document(status) for status in result.statuses],
303
312
  "context": result.context,
304
313
  "context_sha256": result.context_sha256,
@@ -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="output format (default: markdown)",
41
+ help=(
42
+ "output format (default: markdown); json-compact emits schema "
43
+ "version 2 without duplicated source bodies"
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
- output = (
56
- result.context.encode("utf-8")
57
- if arguments.format == "markdown"
58
- else render_json(result)
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
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "context-loader"
7
- version = "1.1.0"
7
+ version = "1.2.0"
8
8
  description = "Render deterministic local Git context for any executor"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12,<3.13"
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "context-loader"
7
- version = "1.1.0"
7
+ version = "1.2.0"
8
8
  description = "Render deterministic local Git context for any executor"
9
9
  license = { file = "LICENSE" }
10
10
  readme = "README.md"
@@ -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": 2,
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.1.0"
49
+ "tool_version": "1.2.0"
48
50
  }
File without changes