codecrate 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
Potentially problematic release.
This version of codecrate might be problematic. Click here for more details.
- codecrate/_version.py +2 -2
- codecrate/cli.py +238 -56
- {codecrate-0.1.1.dist-info → codecrate-0.1.2.dist-info}/METADATA +1 -1
- {codecrate-0.1.1.dist-info → codecrate-0.1.2.dist-info}/RECORD +8 -8
- {codecrate-0.1.1.dist-info → codecrate-0.1.2.dist-info}/WHEEL +0 -0
- {codecrate-0.1.1.dist-info → codecrate-0.1.2.dist-info}/entry_points.txt +0 -0
- {codecrate-0.1.1.dist-info → codecrate-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {codecrate-0.1.1.dist-info → codecrate-0.1.2.dist-info}/top_level.txt +0 -0
codecrate/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
31
|
+
__version__ = version = '0.1.2'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 2)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
codecrate/cli.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
+
from dataclasses import dataclass
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
|
-
from .config import load_config
|
|
7
|
+
from .config import Config, load_config
|
|
7
8
|
from .diffgen import generate_patch_markdown
|
|
8
9
|
from .discover import discover_files
|
|
9
10
|
from .markdown import render_markdown
|
|
@@ -22,8 +23,22 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
22
23
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
23
24
|
|
|
24
25
|
# pack
|
|
25
|
-
pack = sub.add_parser(
|
|
26
|
-
|
|
26
|
+
pack = sub.add_parser(
|
|
27
|
+
"pack", help="Pack one or more repositories/directories into Markdown."
|
|
28
|
+
)
|
|
29
|
+
pack.add_argument(
|
|
30
|
+
"root",
|
|
31
|
+
type=Path,
|
|
32
|
+
nargs="?",
|
|
33
|
+
help="Root directory to scan (omit when using --repo)",
|
|
34
|
+
)
|
|
35
|
+
pack.add_argument(
|
|
36
|
+
"--repo",
|
|
37
|
+
action="append",
|
|
38
|
+
default=None,
|
|
39
|
+
type=Path,
|
|
40
|
+
help="Additional repo root to pack (repeatable; use instead of ROOT)",
|
|
41
|
+
)
|
|
27
42
|
pack.add_argument(
|
|
28
43
|
"-o",
|
|
29
44
|
"--output",
|
|
@@ -123,6 +138,135 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
123
138
|
return p
|
|
124
139
|
|
|
125
140
|
|
|
141
|
+
@dataclass(frozen=True)
|
|
142
|
+
class PackOptions:
|
|
143
|
+
include: list[str] | None
|
|
144
|
+
exclude: list[str] | None
|
|
145
|
+
keep_docstrings: bool
|
|
146
|
+
include_manifest: bool
|
|
147
|
+
respect_gitignore: bool
|
|
148
|
+
dedupe: bool
|
|
149
|
+
split_max_chars: int
|
|
150
|
+
layout: str
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@dataclass(frozen=True)
|
|
154
|
+
class PackRun:
|
|
155
|
+
root: Path
|
|
156
|
+
label: str
|
|
157
|
+
slug: str
|
|
158
|
+
markdown: str
|
|
159
|
+
options: PackOptions
|
|
160
|
+
default_output: Path
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _resolve_pack_options(cfg: Config, args: argparse.Namespace) -> PackOptions:
|
|
164
|
+
include = args.include if args.include is not None else cfg.include
|
|
165
|
+
exclude = args.exclude if args.exclude is not None else cfg.exclude
|
|
166
|
+
keep_docstrings = (
|
|
167
|
+
cfg.keep_docstrings
|
|
168
|
+
if args.keep_docstrings is None
|
|
169
|
+
else bool(args.keep_docstrings)
|
|
170
|
+
)
|
|
171
|
+
include_manifest = cfg.manifest if args.manifest is None else bool(args.manifest)
|
|
172
|
+
respect_gitignore = (
|
|
173
|
+
cfg.respect_gitignore
|
|
174
|
+
if args.respect_gitignore is None
|
|
175
|
+
else bool(args.respect_gitignore)
|
|
176
|
+
)
|
|
177
|
+
dedupe = bool(args.dedupe) or bool(cfg.dedupe)
|
|
178
|
+
split_max_chars = (
|
|
179
|
+
cfg.split_max_chars
|
|
180
|
+
if args.split_max_chars is None
|
|
181
|
+
else int(args.split_max_chars or 0)
|
|
182
|
+
)
|
|
183
|
+
layout = (
|
|
184
|
+
str(args.layout).strip().lower()
|
|
185
|
+
if args.layout is not None
|
|
186
|
+
else str(getattr(cfg, "layout", "auto")).strip().lower()
|
|
187
|
+
)
|
|
188
|
+
return PackOptions(
|
|
189
|
+
include=include,
|
|
190
|
+
exclude=exclude,
|
|
191
|
+
keep_docstrings=keep_docstrings,
|
|
192
|
+
include_manifest=include_manifest,
|
|
193
|
+
respect_gitignore=respect_gitignore,
|
|
194
|
+
dedupe=dedupe,
|
|
195
|
+
split_max_chars=split_max_chars,
|
|
196
|
+
layout=layout,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _resolve_output_path(cfg: Config, args: argparse.Namespace, root: Path) -> Path:
|
|
201
|
+
if args.output is not None:
|
|
202
|
+
return args.output
|
|
203
|
+
out_path = Path(getattr(cfg, "output", "context.md"))
|
|
204
|
+
if not out_path.is_absolute():
|
|
205
|
+
out_path = root / out_path
|
|
206
|
+
return out_path
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _default_repo_label(root: Path) -> str:
|
|
210
|
+
cwd = Path.cwd().resolve()
|
|
211
|
+
resolved = root.resolve()
|
|
212
|
+
try:
|
|
213
|
+
rel = resolved.relative_to(cwd).as_posix()
|
|
214
|
+
return rel or resolved.name or resolved.as_posix()
|
|
215
|
+
except ValueError:
|
|
216
|
+
return root.name or resolved.name or resolved.as_posix()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _unique_label(root: Path, used: set[str]) -> str:
|
|
220
|
+
base = _default_repo_label(root)
|
|
221
|
+
label = base
|
|
222
|
+
idx = 2
|
|
223
|
+
while label in used:
|
|
224
|
+
label = f"{base}-{idx}"
|
|
225
|
+
idx += 1
|
|
226
|
+
used.add(label)
|
|
227
|
+
return label
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _slugify(label: str) -> str:
|
|
231
|
+
safe: list[str] = []
|
|
232
|
+
for ch in label:
|
|
233
|
+
if ch.isalnum() or ch in {"-", "_"}:
|
|
234
|
+
safe.append(ch)
|
|
235
|
+
else:
|
|
236
|
+
safe.append("-")
|
|
237
|
+
slug = "".join(safe).strip("-")
|
|
238
|
+
while "--" in slug:
|
|
239
|
+
slug = slug.replace("--", "-")
|
|
240
|
+
return slug or "repo"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _unique_slug(label: str, used: set[str]) -> str:
|
|
244
|
+
base = _slugify(label)
|
|
245
|
+
slug = base
|
|
246
|
+
idx = 2
|
|
247
|
+
while slug in used:
|
|
248
|
+
slug = f"{base}-{idx}"
|
|
249
|
+
idx += 1
|
|
250
|
+
used.add(slug)
|
|
251
|
+
return slug
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _prefix_repo_header(text: str, label: str) -> str:
|
|
255
|
+
header = f"# Repository: {label}\n\n"
|
|
256
|
+
if text.startswith(header):
|
|
257
|
+
return text
|
|
258
|
+
return header + text
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _combine_pack_markdown(packs: list[PackRun]) -> str:
|
|
262
|
+
out: list[str] = []
|
|
263
|
+
for i, pack in enumerate(packs):
|
|
264
|
+
if i:
|
|
265
|
+
out.append("\n\n")
|
|
266
|
+
out.append(_prefix_repo_header(pack.markdown.rstrip() + "\n", pack.label))
|
|
267
|
+
return "".join(out).rstrip() + "\n"
|
|
268
|
+
|
|
269
|
+
|
|
126
270
|
def _extract_diff_blocks(md_text: str) -> str:
|
|
127
271
|
"""
|
|
128
272
|
Extract only diff fences from markdown and concatenate to a unified diff string.
|
|
@@ -140,73 +284,111 @@ def _extract_diff_blocks(md_text: str) -> str:
|
|
|
140
284
|
return "\n".join(out) + "\n"
|
|
141
285
|
|
|
142
286
|
|
|
143
|
-
def main(argv: list[str] | None = None) -> None:
|
|
287
|
+
def main(argv: list[str] | None = None) -> None: # noqa: C901
|
|
144
288
|
parser = build_parser()
|
|
145
289
|
args = parser.parse_args(argv)
|
|
146
290
|
|
|
147
291
|
if args.cmd == "pack":
|
|
148
|
-
|
|
149
|
-
|
|
292
|
+
if args.repo:
|
|
293
|
+
if args.root is not None:
|
|
294
|
+
parser.error(
|
|
295
|
+
"pack: specify either ROOT or --repo (repeatable), not both"
|
|
296
|
+
)
|
|
297
|
+
roots = [r.resolve() for r in args.repo]
|
|
298
|
+
else:
|
|
299
|
+
if args.root is None:
|
|
300
|
+
parser.error("pack: ROOT is required when --repo is not used")
|
|
301
|
+
roots = [args.root.resolve()]
|
|
150
302
|
|
|
151
|
-
|
|
152
|
-
|
|
303
|
+
used_labels: set[str] = set()
|
|
304
|
+
used_slugs: set[str] = set()
|
|
305
|
+
pack_runs: list[PackRun] = []
|
|
153
306
|
|
|
154
|
-
|
|
155
|
-
cfg
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
307
|
+
for root in roots:
|
|
308
|
+
cfg = load_config(root)
|
|
309
|
+
options = _resolve_pack_options(cfg, args)
|
|
310
|
+
label = _unique_label(root, used_labels)
|
|
311
|
+
slug = _unique_slug(label, used_slugs)
|
|
312
|
+
|
|
313
|
+
disc = discover_files(
|
|
314
|
+
root=root,
|
|
315
|
+
include=options.include,
|
|
316
|
+
exclude=options.exclude,
|
|
317
|
+
respect_gitignore=options.respect_gitignore,
|
|
318
|
+
)
|
|
319
|
+
pack, canonical = pack_repo(
|
|
320
|
+
disc.root,
|
|
321
|
+
disc.files,
|
|
322
|
+
keep_docstrings=options.keep_docstrings,
|
|
323
|
+
dedupe=options.dedupe,
|
|
324
|
+
)
|
|
325
|
+
md = render_markdown(
|
|
326
|
+
pack,
|
|
327
|
+
canonical,
|
|
328
|
+
layout=options.layout,
|
|
329
|
+
include_manifest=options.include_manifest,
|
|
330
|
+
)
|
|
331
|
+
default_output = _resolve_output_path(cfg, args, root)
|
|
332
|
+
pack_runs.append(
|
|
333
|
+
PackRun(
|
|
334
|
+
root=root,
|
|
335
|
+
label=label,
|
|
336
|
+
slug=slug,
|
|
337
|
+
markdown=md,
|
|
338
|
+
options=options,
|
|
339
|
+
default_output=default_output,
|
|
340
|
+
)
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
out_path = (
|
|
344
|
+
args.output if args.output is not None else pack_runs[0].default_output
|
|
177
345
|
)
|
|
178
|
-
if
|
|
179
|
-
|
|
346
|
+
if len(pack_runs) == 1:
|
|
347
|
+
md = pack_runs[0].markdown
|
|
180
348
|
else:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
out_path = root / out_path
|
|
184
|
-
disc = discover_files(
|
|
185
|
-
root=root,
|
|
186
|
-
include=include,
|
|
187
|
-
exclude=exclude,
|
|
188
|
-
respect_gitignore=respect_gitignore,
|
|
189
|
-
)
|
|
190
|
-
pack, canonical = pack_repo(
|
|
191
|
-
disc.root, disc.files, keep_docstrings=keep_docstrings, dedupe=dedupe
|
|
192
|
-
)
|
|
193
|
-
md = render_markdown(
|
|
194
|
-
pack, canonical, layout=layout, include_manifest=include_manifest
|
|
195
|
-
)
|
|
349
|
+
md = _combine_pack_markdown(pack_runs)
|
|
350
|
+
|
|
196
351
|
# Always write the canonical, unsplit pack
|
|
197
352
|
# for machine parsing (unpack/validate).
|
|
198
353
|
out_path.write_text(md, encoding="utf-8")
|
|
199
354
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
355
|
+
extra_count = 0
|
|
356
|
+
if len(pack_runs) == 1:
|
|
357
|
+
split_max_chars = pack_runs[0].options.split_max_chars
|
|
358
|
+
parts = split_by_max_chars(md, out_path, split_max_chars)
|
|
359
|
+
extra = [p for p in parts if p.path != out_path]
|
|
360
|
+
for part in extra:
|
|
361
|
+
part.path.write_text(part.content, encoding="utf-8")
|
|
362
|
+
extra_count += len(extra)
|
|
363
|
+
else:
|
|
364
|
+
for pack in pack_runs:
|
|
365
|
+
if pack.options.split_max_chars <= 0:
|
|
366
|
+
continue
|
|
367
|
+
repo_base = out_path.with_name(
|
|
368
|
+
f"{out_path.stem}.{pack.slug}{out_path.suffix}"
|
|
369
|
+
)
|
|
370
|
+
parts = split_by_max_chars(
|
|
371
|
+
pack.markdown, repo_base, pack.options.split_max_chars
|
|
372
|
+
)
|
|
373
|
+
extra = [p for p in parts if p.path != repo_base]
|
|
374
|
+
for part in extra:
|
|
375
|
+
content = _prefix_repo_header(part.content, pack.label)
|
|
376
|
+
part.path.write_text(content, encoding="utf-8")
|
|
377
|
+
extra_count += len(extra)
|
|
205
378
|
|
|
206
|
-
if
|
|
207
|
-
|
|
379
|
+
if extra_count:
|
|
380
|
+
if len(pack_runs) == 1:
|
|
381
|
+
print(f"Wrote {out_path} and {extra_count} split part file(s).")
|
|
382
|
+
else:
|
|
383
|
+
print(
|
|
384
|
+
f"Wrote {out_path} and {extra_count} split part file(s) for "
|
|
385
|
+
f"{len(pack_runs)} repos."
|
|
386
|
+
)
|
|
208
387
|
else:
|
|
209
|
-
|
|
388
|
+
if len(pack_runs) == 1:
|
|
389
|
+
print(f"Wrote {out_path}.")
|
|
390
|
+
else:
|
|
391
|
+
print(f"Wrote {out_path} for {len(pack_runs)} repos.")
|
|
210
392
|
elif args.cmd == "unpack":
|
|
211
393
|
md_text = args.markdown.read_text(encoding="utf-8", errors="replace")
|
|
212
394
|
unpack_to_dir(md_text, args.out_dir)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
codecrate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
codecrate/_version.py,sha256=
|
|
3
|
-
codecrate/cli.py,sha256=
|
|
2
|
+
codecrate/_version.py,sha256=Ok5oAXdWgR9aghaFXTafTeDW6sYO3uVe6d2Nket57R4,704
|
|
3
|
+
codecrate/cli.py,sha256=AXURc8QeFzR0HvbhittK38g4N1xr4al564uCBq0PTlY,13644
|
|
4
4
|
codecrate/config.py,sha256=8VOmpjHC3am6LRFnyHUY3376947XTvSFGp3fUxSZgOk,3523
|
|
5
5
|
codecrate/diffgen.py,sha256=xMH-wJeMox_xoMLrGck2o7RP5mjlCJjndSHPHql9CJg,3432
|
|
6
6
|
codecrate/discover.py,sha256=eWebPPbPHy5WrtbCX-RA1hKiPyQau6buUm5OcjpVi9U,2847
|
|
@@ -16,9 +16,9 @@ codecrate/token_budget.py,sha256=0vG9h7l5yNn909QQEItrYQJIjMT2h5LcM9iZf4zu-3c,129
|
|
|
16
16
|
codecrate/udiff.py,sha256=bMvwhgqXZw2pIW5VcRRATUoDspM2XGQU3nInD_A7WWo,6249
|
|
17
17
|
codecrate/unpacker.py,sha256=soh6qwRE6sxoBAbr3UbXk1O5aiwdnfUhkqfFdSvU0_I,5063
|
|
18
18
|
codecrate/validate.py,sha256=-KVU7RQ8zJG-YJoD8kLCYgE0OWWdy-yYzl0gyp-3mWo,4250
|
|
19
|
-
codecrate-0.1.
|
|
20
|
-
codecrate-0.1.
|
|
21
|
-
codecrate-0.1.
|
|
22
|
-
codecrate-0.1.
|
|
23
|
-
codecrate-0.1.
|
|
24
|
-
codecrate-0.1.
|
|
19
|
+
codecrate-0.1.2.dist-info/licenses/LICENSE,sha256=O6yVC2oL8vUoAA3oPEvLSbvxzmwtOOPiY7dOZzgrxi0,1074
|
|
20
|
+
codecrate-0.1.2.dist-info/METADATA,sha256=9GAqz6NFaQ1g9uRKKYw6hn4RVhmO5lemKle54PVDoK4,9394
|
|
21
|
+
codecrate-0.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
22
|
+
codecrate-0.1.2.dist-info/entry_points.txt,sha256=DcY9tib-PzdLebck4B2RYJ0CGH6cqAJMCHPU3MdA0Dk,49
|
|
23
|
+
codecrate-0.1.2.dist-info/top_level.txt,sha256=-jD2a_aH1iQN4atRhGw7ZhEYnOWe88nfmkz6sPZ6WEg,10
|
|
24
|
+
codecrate-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|