context-engineering-cli 2.6.0__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.
- context_engineering/__init__.py +3 -0
- context_engineering/__main__.py +2 -0
- context_engineering/analysis/__init__.py +1 -0
- context_engineering/analysis/backfill.py +1064 -0
- context_engineering/analysis/context_check.py +253 -0
- context_engineering/analysis/context_layout.py +111 -0
- context_engineering/analysis/context_review.py +224 -0
- context_engineering/analysis/cross_cutting/__init__.py +6 -0
- context_engineering/analysis/cross_cutting/authors.py +57 -0
- context_engineering/analysis/cross_cutting/buckets.py +40 -0
- context_engineering/analysis/cross_cutting/co_change.py +47 -0
- context_engineering/analysis/cross_cutting/discover.py +75 -0
- context_engineering/analysis/cross_cutting/imports.py +61 -0
- context_engineering/analysis/cross_cutting/pair.py +118 -0
- context_engineering/analysis/impact.py +77 -0
- context_engineering/analysis/sessions.py +27 -0
- context_engineering/analysis/staleness.py +179 -0
- context_engineering/analysis/tier.py +91 -0
- context_engineering/checks/__init__.py +1 -0
- context_engineering/checks/antipatterns/__init__.py +5 -0
- context_engineering/checks/antipatterns/context.py +23 -0
- context_engineering/checks/antipatterns/density.py +72 -0
- context_engineering/checks/antipatterns/line_limits.py +52 -0
- context_engineering/checks/antipatterns/runner.py +137 -0
- context_engineering/checks/antipatterns/splitting.py +97 -0
- context_engineering/checks/antipatterns/volatile.py +38 -0
- context_engineering/checks/antipatterns/watermark.py +113 -0
- context_engineering/checks/contracts.py +456 -0
- context_engineering/checks/depth.py +82 -0
- context_engineering/checks/frontmatter.py +125 -0
- context_engineering/checks/references.py +325 -0
- context_engineering/checks/skill_structure.py +124 -0
- context_engineering/cli/__init__.py +3 -0
- context_engineering/cli/dispatch.py +90 -0
- context_engineering/cli/registry.py +33 -0
- context_engineering/cli/render.py +92 -0
- context_engineering/cli/subcommands.py +587 -0
- context_engineering/domain/__init__.py +0 -0
- context_engineering/domain/commit.py +19 -0
- context_engineering/domain/evidence.py +57 -0
- context_engineering/domain/finding.py +37 -0
- context_engineering/domain/result.py +59 -0
- context_engineering/infra/__init__.py +13 -0
- context_engineering/infra/filesystem.py +22 -0
- context_engineering/infra/git.py +153 -0
- context_engineering/infra/git_evidence.py +357 -0
- context_engineering/infra/git_tree.py +139 -0
- context_engineering/infra/markdown.py +58 -0
- context_engineering/infra/yaml_frontmatter.py +70 -0
- context_engineering_cli-2.6.0.dist-info/METADATA +27 -0
- context_engineering_cli-2.6.0.dist-info/RECORD +55 -0
- context_engineering_cli-2.6.0.dist-info/WHEEL +4 -0
- context_engineering_cli-2.6.0.dist-info/entry_points.txt +2 -0
- context_engineering_cli-2.6.0.dist-info/licenses/LICENSE +21 -0
- provenance.json +1 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Read an exact Git tree through a temporary, disposable filesystem view."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import posixpath
|
|
7
|
+
import stat
|
|
8
|
+
import subprocess
|
|
9
|
+
from collections.abc import Iterator
|
|
10
|
+
from contextlib import contextmanager
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from pathlib import Path, PurePosixPath
|
|
13
|
+
from tempfile import TemporaryDirectory
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class _TreeEntry:
|
|
18
|
+
mode: str
|
|
19
|
+
object_id: str
|
|
20
|
+
path: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _tree_entries(repo: Path, commit: str) -> tuple[list[_TreeEntry], str | None]:
|
|
24
|
+
try:
|
|
25
|
+
result = subprocess.run(
|
|
26
|
+
["git", "ls-tree", "-r", "-z", "--full-tree", commit],
|
|
27
|
+
cwd=repo,
|
|
28
|
+
capture_output=True,
|
|
29
|
+
check=False,
|
|
30
|
+
timeout=120,
|
|
31
|
+
)
|
|
32
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
33
|
+
return [], f"could not read Git tree: {exc}"
|
|
34
|
+
if result.returncode != 0:
|
|
35
|
+
message = os.fsdecode(result.stderr).strip() or "git ls-tree failed"
|
|
36
|
+
return [], message
|
|
37
|
+
entries: list[_TreeEntry] = []
|
|
38
|
+
for record in result.stdout.split(b"\0"):
|
|
39
|
+
if not record:
|
|
40
|
+
continue
|
|
41
|
+
metadata, separator, raw_path = record.partition(b"\t")
|
|
42
|
+
fields = metadata.split()
|
|
43
|
+
if not separator or len(fields) != 3:
|
|
44
|
+
return [], "git ls-tree returned a malformed record"
|
|
45
|
+
mode, object_type, object_id = (os.fsdecode(field) for field in fields)
|
|
46
|
+
path = os.fsdecode(raw_path)
|
|
47
|
+
pure = PurePosixPath(path)
|
|
48
|
+
if pure.is_absolute() or any(part in {"", ".", ".."} for part in pure.parts):
|
|
49
|
+
return [], f"git tree contains an unsafe path: {path!r}"
|
|
50
|
+
if object_type == "blob":
|
|
51
|
+
entries.append(_TreeEntry(mode=mode, object_id=object_id, path=path))
|
|
52
|
+
elif mode == "160000":
|
|
53
|
+
entries.append(_TreeEntry(mode=mode, object_id=object_id, path=path))
|
|
54
|
+
return entries, None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _read_blobs(
|
|
58
|
+
repo: Path, entries: list[_TreeEntry]
|
|
59
|
+
) -> tuple[dict[str, bytes], str | None]:
|
|
60
|
+
object_ids = list(dict.fromkeys(entry.object_id for entry in entries if entry.mode != "160000"))
|
|
61
|
+
if not object_ids:
|
|
62
|
+
return {}, None
|
|
63
|
+
try:
|
|
64
|
+
result = subprocess.run(
|
|
65
|
+
["git", "cat-file", "--batch"],
|
|
66
|
+
cwd=repo,
|
|
67
|
+
input=("\n".join(object_ids) + "\n").encode(),
|
|
68
|
+
capture_output=True,
|
|
69
|
+
check=False,
|
|
70
|
+
timeout=120,
|
|
71
|
+
)
|
|
72
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
73
|
+
return {}, f"could not read Git blobs: {exc}"
|
|
74
|
+
if result.returncode != 0:
|
|
75
|
+
message = os.fsdecode(result.stderr).strip() or "git cat-file failed"
|
|
76
|
+
return {}, message
|
|
77
|
+
blobs: dict[str, bytes] = {}
|
|
78
|
+
output = result.stdout
|
|
79
|
+
offset = 0
|
|
80
|
+
for expected in object_ids:
|
|
81
|
+
newline = output.find(b"\n", offset)
|
|
82
|
+
if newline < 0:
|
|
83
|
+
return {}, "git cat-file returned a truncated header"
|
|
84
|
+
fields = output[offset:newline].split()
|
|
85
|
+
if len(fields) != 3 or os.fsdecode(fields[0]) != expected or fields[1] != b"blob":
|
|
86
|
+
return {}, "git cat-file returned unexpected object metadata"
|
|
87
|
+
try:
|
|
88
|
+
size = int(fields[2])
|
|
89
|
+
except ValueError:
|
|
90
|
+
return {}, "git cat-file returned an invalid blob size"
|
|
91
|
+
start = newline + 1
|
|
92
|
+
end = start + size
|
|
93
|
+
if end >= len(output) or output[end : end + 1] != b"\n":
|
|
94
|
+
return {}, "git cat-file returned truncated blob content"
|
|
95
|
+
blobs[expected] = output[start:end]
|
|
96
|
+
offset = end + 1
|
|
97
|
+
if offset != len(output):
|
|
98
|
+
return {}, "git cat-file returned trailing data"
|
|
99
|
+
return blobs, None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@contextmanager
|
|
103
|
+
def materialize_tree(repo: Path, commit: str) -> Iterator[tuple[Path | None, str | None]]:
|
|
104
|
+
"""Yield tracked tree bytes without checkout filters or archive attributes."""
|
|
105
|
+
entries, entry_error = _tree_entries(repo, commit)
|
|
106
|
+
if entry_error:
|
|
107
|
+
yield None, entry_error
|
|
108
|
+
return
|
|
109
|
+
blobs, blob_error = _read_blobs(repo, entries)
|
|
110
|
+
if blob_error:
|
|
111
|
+
yield None, blob_error
|
|
112
|
+
return
|
|
113
|
+
with TemporaryDirectory(prefix="context-engineering-tree-") as temporary:
|
|
114
|
+
root = Path(temporary)
|
|
115
|
+
try:
|
|
116
|
+
for entry in entries:
|
|
117
|
+
target = root.joinpath(*PurePosixPath(entry.path).parts)
|
|
118
|
+
if entry.mode == "160000":
|
|
119
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
continue
|
|
121
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
content = blobs[entry.object_id]
|
|
123
|
+
if entry.mode == "120000":
|
|
124
|
+
link_text = os.fsdecode(content)
|
|
125
|
+
link = PurePosixPath(link_text)
|
|
126
|
+
normalized = posixpath.normpath(
|
|
127
|
+
str(PurePosixPath(entry.path).parent / link)
|
|
128
|
+
)
|
|
129
|
+
if link.is_absolute() or normalized == ".." or normalized.startswith("../"):
|
|
130
|
+
raise OSError(f"tracked symlink escapes the Git tree: {entry.path!r}")
|
|
131
|
+
target.symlink_to(link_text)
|
|
132
|
+
continue
|
|
133
|
+
target.write_bytes(content)
|
|
134
|
+
if entry.mode == "100755":
|
|
135
|
+
target.chmod(target.stat().st_mode | stat.S_IXUSR)
|
|
136
|
+
except OSError as exc:
|
|
137
|
+
yield None, f"could not materialize Git tree: {exc}"
|
|
138
|
+
return
|
|
139
|
+
yield root, None
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Shared Markdown visibility helpers for deterministic context checks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
_HTML_COMMENT_RE = re.compile(r"<!--.*?(?:-->|$)", re.DOTALL)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _masked_line(line: str) -> str:
|
|
11
|
+
return "".join(
|
|
12
|
+
"\n" if character == "\n" else "\r" if character == "\r" else " " for character in line
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _fence_marker(line: str) -> tuple[str, int] | None:
|
|
17
|
+
text = line.rstrip("\r\n")
|
|
18
|
+
indentation = len(text) - len(text.lstrip(" "))
|
|
19
|
+
if indentation > 3:
|
|
20
|
+
return None
|
|
21
|
+
body = text[indentation:]
|
|
22
|
+
if not body or body[0] not in {"`", "~"}:
|
|
23
|
+
return None
|
|
24
|
+
marker = body[0]
|
|
25
|
+
length = len(body) - len(body.lstrip(marker))
|
|
26
|
+
return (marker, length) if length >= 3 else None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _closes_fence(line: str, marker: str, minimum_length: int) -> bool:
|
|
30
|
+
text = line.rstrip("\r\n")
|
|
31
|
+
indentation = len(text) - len(text.lstrip(" "))
|
|
32
|
+
if indentation > 3:
|
|
33
|
+
return False
|
|
34
|
+
body = text[indentation:]
|
|
35
|
+
length = len(body) - len(body.lstrip(marker))
|
|
36
|
+
return length >= minimum_length and not body[length:].strip()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def mask_invisible_markdown(content: str) -> str:
|
|
40
|
+
"""Replace fenced blocks and HTML comments while preserving line positions."""
|
|
41
|
+
visible: list[str] = []
|
|
42
|
+
marker: str | None = None
|
|
43
|
+
minimum_length = 0
|
|
44
|
+
for line in content.splitlines(keepends=True):
|
|
45
|
+
if marker is not None:
|
|
46
|
+
visible.append(_masked_line(line))
|
|
47
|
+
if _closes_fence(line, marker, minimum_length):
|
|
48
|
+
marker = None
|
|
49
|
+
minimum_length = 0
|
|
50
|
+
continue
|
|
51
|
+
opening = _fence_marker(line)
|
|
52
|
+
if opening is None:
|
|
53
|
+
visible.append(line)
|
|
54
|
+
continue
|
|
55
|
+
marker, minimum_length = opening
|
|
56
|
+
visible.append(_masked_line(line))
|
|
57
|
+
without_fences = "".join(visible)
|
|
58
|
+
return _HTML_COMMENT_RE.sub(lambda match: _masked_line(match.group(0)), without_fences)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""One frontmatter parser. Replaces the two hand-rolled parsers in the codebase.
|
|
2
|
+
|
|
3
|
+
We don't pull in PyYAML — the schema is tightly constrained (kebab-cased top-level
|
|
4
|
+
keys, optional scalar values, optional list of inline dicts). This parser handles
|
|
5
|
+
everything the plugin emits and nothing more.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
_TOP_KEY_RE = re.compile(r"^([a-z][a-z0-9_-]*):\s*(.*)$")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def parse_frontmatter(content: str) -> tuple[dict | None, str]:
|
|
16
|
+
"""Return (frontmatter_dict, body). `None` if content has no frontmatter."""
|
|
17
|
+
if not content.startswith("---"):
|
|
18
|
+
return None, content
|
|
19
|
+
|
|
20
|
+
end = content.find("\n---", 3)
|
|
21
|
+
if end == -1:
|
|
22
|
+
return None, content
|
|
23
|
+
|
|
24
|
+
raw = content[3:end].strip()
|
|
25
|
+
body = content[end + 4:].lstrip("\n")
|
|
26
|
+
|
|
27
|
+
fm: dict = {}
|
|
28
|
+
current_key: str | None = None
|
|
29
|
+
current_list: list | None = None
|
|
30
|
+
|
|
31
|
+
for line in raw.splitlines():
|
|
32
|
+
if not line.strip() or line.strip().startswith("#"):
|
|
33
|
+
continue
|
|
34
|
+
|
|
35
|
+
top = _TOP_KEY_RE.match(line) if not line.startswith(" ") else None
|
|
36
|
+
if top:
|
|
37
|
+
if current_key is not None and current_list is not None:
|
|
38
|
+
fm[current_key] = current_list
|
|
39
|
+
current_list = None
|
|
40
|
+
|
|
41
|
+
key, value = top.group(1), top.group(2).strip()
|
|
42
|
+
if value == "":
|
|
43
|
+
current_key = key
|
|
44
|
+
current_list = None
|
|
45
|
+
elif value == "[]":
|
|
46
|
+
fm[key] = []
|
|
47
|
+
current_key = key
|
|
48
|
+
current_list = None
|
|
49
|
+
else:
|
|
50
|
+
fm[key] = value
|
|
51
|
+
current_key = key
|
|
52
|
+
current_list = None
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
stripped = line.strip()
|
|
56
|
+
if stripped.startswith("- ") and current_key is not None:
|
|
57
|
+
item = stripped[2:].strip()
|
|
58
|
+
if current_list is None:
|
|
59
|
+
current_list = []
|
|
60
|
+
if ":" in item and item.split(":", 1)[0].strip().isidentifier():
|
|
61
|
+
# Inline dict entry like `- id: foo`
|
|
62
|
+
k, _, v = item.partition(":")
|
|
63
|
+
current_list.append({k.strip(): v.strip()})
|
|
64
|
+
else:
|
|
65
|
+
current_list.append(item)
|
|
66
|
+
|
|
67
|
+
if current_key is not None and current_list is not None:
|
|
68
|
+
fm[current_key] = current_list
|
|
69
|
+
|
|
70
|
+
return fm, body
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: context-engineering-cli
|
|
3
|
+
Version: 2.6.0
|
|
4
|
+
Summary: Portable CLI for Context Engineering analyzers.
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 safurrier
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Python: >=3.11
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
context_engineering/__init__.py,sha256=3mgr87eJEJl9yhhFAzNPn6xHcy7EOov6KBuab6x3nU4,76
|
|
2
|
+
context_engineering/__main__.py,sha256=fi_tkPTbyCAM_UhTg1qcz10jmie3vNrg50QOOU4mX-s,75
|
|
3
|
+
context_engineering/analysis/__init__.py,sha256=1Sii4fbwxLJukSHqDYihcLV5fErPI6kRJumpa_rplNg,64
|
|
4
|
+
context_engineering/analysis/backfill.py,sha256=T7elnNsy7aeWpUPXAaQF5XRhh321tcZLv1sc8DczMDg,39108
|
|
5
|
+
context_engineering/analysis/context_check.py,sha256=lsjgMwyLUALlFc36T6Hsuwt3pND7fjEP8lbEbyuiSDo,9979
|
|
6
|
+
context_engineering/analysis/context_layout.py,sha256=MwZNgiEIUdhB9v92eyjskqMWYsCf2Y2i6zjf0027tco,3790
|
|
7
|
+
context_engineering/analysis/context_review.py,sha256=KFJG900geWdvovTpP28Y9JWYXMVi9XIRHsKvicA76zY,7501
|
|
8
|
+
context_engineering/analysis/impact.py,sha256=5f1_QCRdCklbVwQC1r3NlWJXXNPRp1twGPN1CdEgrhM,2736
|
|
9
|
+
context_engineering/analysis/sessions.py,sha256=NCjqN-xOY4dhktglcIQpxSWVoRFCHE_lVZEntCyNgWk,1439
|
|
10
|
+
context_engineering/analysis/staleness.py,sha256=HC3IVRlyu7e-S7vn3DzewFb8RWNpfZ8eyXQi36as85k,6153
|
|
11
|
+
context_engineering/analysis/tier.py,sha256=91ORWsJ_laQxUPI9JfQfBi0ygI31Jx7kRfX0LFCfU3g,2687
|
|
12
|
+
context_engineering/analysis/cross_cutting/__init__.py,sha256=RcDjy89T0zU2W-iI7fi1TJYf2pQOD_0Oo9BHlccMuMk,162
|
|
13
|
+
context_engineering/analysis/cross_cutting/authors.py,sha256=bYT1d1yZKT0qqM8d20_KC0lMlUOJkfT0igVovuv8mtk,1930
|
|
14
|
+
context_engineering/analysis/cross_cutting/buckets.py,sha256=3aqn-g0jK1MpejOfqE13cGwezOc5YEhBEwgg_qwJTYM,1354
|
|
15
|
+
context_engineering/analysis/cross_cutting/co_change.py,sha256=7tovGwRvI1I5mzuEXFWkEgLaCYeu9nDDqwAakbMr_gY,1591
|
|
16
|
+
context_engineering/analysis/cross_cutting/discover.py,sha256=uzNflqhI_8H__V7PLKlZFTB_ONm9VNZ41EK5_LIEVFQ,2507
|
|
17
|
+
context_engineering/analysis/cross_cutting/imports.py,sha256=kkZ8-uKqtTHbl1igS2kEe3eOeYWnyxqyNVVBZlOTCOU,2026
|
|
18
|
+
context_engineering/analysis/cross_cutting/pair.py,sha256=B91LFQ04EeTuffdtk8Kbfg4JalKsGNTXhEZZmkiElWg,4215
|
|
19
|
+
context_engineering/checks/__init__.py,sha256=G1oNPg6aUrqf9P70T7sBIC6W3xdWwJD8bJ5hXSO4Xcg,51
|
|
20
|
+
context_engineering/checks/contracts.py,sha256=B6EEWfZcAwAoHeolRiBjdOPebJqKmL_OB6VC5WWJgaQ,15631
|
|
21
|
+
context_engineering/checks/depth.py,sha256=sFY-42FvnvgunEwGgNRtQIca_MzgF1W3-cSu8V6-dyc,2963
|
|
22
|
+
context_engineering/checks/frontmatter.py,sha256=s1V5w75i7GwyZhLU7zladXdp_V804o3Z1Po38SBAFmU,4436
|
|
23
|
+
context_engineering/checks/references.py,sha256=28ZyXwHgCxxKC3BKDwbbo9QaYS98T9_UgJ6KrR7V44k,11370
|
|
24
|
+
context_engineering/checks/skill_structure.py,sha256=Tw97b3TnPQPNX_Wg91VvS8lkAeW97ianhEhwH7wKbQA,4513
|
|
25
|
+
context_engineering/checks/antipatterns/__init__.py,sha256=46H9dq5qr8T6sgDWY7V8r2Ayg2Gx_QoDXS_bGrgOow0,117
|
|
26
|
+
context_engineering/checks/antipatterns/context.py,sha256=_QklypUB9PJU0E86UC6GZnSCU_DJ03lzm9WZtMg4DeQ,675
|
|
27
|
+
context_engineering/checks/antipatterns/density.py,sha256=D9-zIoJUsReHPPV6DEB3CI3WeWf13vnolOpUNLESl1s,2206
|
|
28
|
+
context_engineering/checks/antipatterns/line_limits.py,sha256=WvH-H59NK6-XQ3xHaSCRbmrUpfYx2claOLsFQH4SzXk,1858
|
|
29
|
+
context_engineering/checks/antipatterns/runner.py,sha256=tY08CKqQ7d94RGI1d1i0XrQ4AnFdd6FRpiSKWmWBvqU,4206
|
|
30
|
+
context_engineering/checks/antipatterns/splitting.py,sha256=QJzV5Ut9dNP-RAHG4eT8wj6hItm1hlRFj6XrdqvowTs,3181
|
|
31
|
+
context_engineering/checks/antipatterns/volatile.py,sha256=8DLC591-dqf64FA-irI7QOA2c5IEwW0ANU60vWlu27w,1343
|
|
32
|
+
context_engineering/checks/antipatterns/watermark.py,sha256=fl4ESKBv8PPHNLM76ETDJydszTHgGhEK4oGQVwaJMl4,3753
|
|
33
|
+
context_engineering/cli/__init__.py,sha256=T8XCbwTmhRg-JFuhIqpiyRw-Dyba0SuaIOjYnzHq_T8,47
|
|
34
|
+
context_engineering/cli/dispatch.py,sha256=zParI8G6_QOHyk2baB0jDGMXGU1MGndaLohQEI9yQKk,3056
|
|
35
|
+
context_engineering/cli/registry.py,sha256=DJyeVcCvpxURW9eWpPKCPBoLxI-S4f3P6nkoWfzrT5U,928
|
|
36
|
+
context_engineering/cli/render.py,sha256=E1fqozVlA5DH8JYuAX5civchezLopjwkMVRDIIuuYys,3122
|
|
37
|
+
context_engineering/cli/subcommands.py,sha256=iIbu7VNnC7DIA3-4_jib-w0vVRAc5paB2ecMsXzvltE,18370
|
|
38
|
+
context_engineering/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
+
context_engineering/domain/commit.py,sha256=trd7vLJw06Cft_TvUItpKIcRSuVnGiwi1dDd-gD1Krw,514
|
|
40
|
+
context_engineering/domain/evidence.py,sha256=0xTgJJgm8nYZVR6N-l5Dg7xJB49OPH5A1peha-3PtF4,1634
|
|
41
|
+
context_engineering/domain/finding.py,sha256=w80d29bWZkJIfTnfRB_TEsWkeCX3NYSEsPb6Sx3zd-M,905
|
|
42
|
+
context_engineering/domain/result.py,sha256=Gr2Tw2J08Nijr7G1WRhj-NSJc675CV8gEqeHZFO2Pg4,1630
|
|
43
|
+
context_engineering/infra/__init__.py,sha256=CCkGe59MnxiVwY3kdfmRGWiN0UKYEMKJGRi5zmVLsZ0,324
|
|
44
|
+
context_engineering/infra/filesystem.py,sha256=4FXrGLBLmYGSVFdSJQrgrzc9Q8Gat7tglGvLjtZ5PGg,713
|
|
45
|
+
context_engineering/infra/git.py,sha256=xpDgjxflk_dGTiOpoU6IOiMIRbeRqZY5PkQvQ4CWl3M,4920
|
|
46
|
+
context_engineering/infra/git_evidence.py,sha256=aXNY9DuHUtDPVeMA7Nqc5SNYQGWdQ-B5gZ2CTYO0054,11679
|
|
47
|
+
context_engineering/infra/git_tree.py,sha256=-PZQbYsyXIYpJNBpP1Zo-Lt71Fbxl7LJfnppG3cmVwc,5390
|
|
48
|
+
context_engineering/infra/markdown.py,sha256=tHSt66r4PTYYRTUIfwIjSEDCeGFtcNBrn-YtAhgrioY,1923
|
|
49
|
+
context_engineering/infra/yaml_frontmatter.py,sha256=yVZamFli5iCUsvdzV2a8VAq_ETqnlciCxcX_iOEQnMY,2290
|
|
50
|
+
provenance.json,sha256=3elXiJzp__2KjnqNhOFW_9bkvApaF6HRlCI3lQCyYyw,599
|
|
51
|
+
context_engineering_cli-2.6.0.dist-info/METADATA,sha256=EO4WBUvvPudA7_HuqjZU-9rMWwl1MqOBzPjzvByl0do,1405
|
|
52
|
+
context_engineering_cli-2.6.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
53
|
+
context_engineering_cli-2.6.0.dist-info/entry_points.txt,sha256=PRSEj7vD7_aYqcfeslJIJ0VsWK2bMPh31QifshEsRis,69
|
|
54
|
+
context_engineering_cli-2.6.0.dist-info/licenses/LICENSE,sha256=a_2IOUCuE6IJu-MvjvKkL8hQnGtMybcXi2SAnR65YIY,1066
|
|
55
|
+
context_engineering_cli-2.6.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 safurrier
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
provenance.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"schema": 1, "decision": "This package contains the independently authored portable Context Engineering CLI.", "entries": [{"path_prefix": "src/context_engineering/", "origin": "same-author portable implementation", "license": "MIT"}, {"path": "LICENSE", "origin": "standard MIT text", "license": "MIT"}, {"path": ".gitignore", "origin": "shared repository build exclusions", "license": "MIT"}, {"path": "pyproject.toml", "origin": "same-author package build and project metadata", "license": "MIT"}, {"path": "provenance.json", "origin": "package-scoped provenance inventory", "license": "MIT"}]}
|