custos-code 0.0.2__tar.gz → 0.0.4__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.
Files changed (39) hide show
  1. {custos_code-0.0.2 → custos_code-0.0.4}/PKG-INFO +13 -1
  2. {custos_code-0.0.2 → custos_code-0.0.4}/README.md +12 -0
  3. {custos_code-0.0.2 → custos_code-0.0.4}/pyproject.toml +1 -1
  4. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/__init__.py +1 -1
  5. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/cli.py +24 -0
  6. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/scope.py +10 -2
  7. {custos_code-0.0.2 → custos_code-0.0.4}/.gitignore +0 -0
  8. {custos_code-0.0.2 → custos_code-0.0.4}/LICENSE +0 -0
  9. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/adapters/__init__.py +0 -0
  10. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/adapters/claude_code.py +0 -0
  11. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/adapters/codex.py +0 -0
  12. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/adapters/copilot.py +0 -0
  13. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/adapters/devin.py +0 -0
  14. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/adapters/machine.py +0 -0
  15. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/adapters/otel.py +0 -0
  16. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/adapters/state.py +0 -0
  17. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/arch.py +0 -0
  18. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/claims.py +0 -0
  19. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/compress.py +0 -0
  20. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/cost.py +0 -0
  21. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/demo_fixtures/__init__.py +0 -0
  22. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/demo_fixtures/ok_tests_0.jsonl +0 -0
  23. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/demo_fixtures/trap_echo_0.jsonl +0 -0
  24. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/demo_fixtures/trap_failing_0.jsonl +0 -0
  25. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/demo_fixtures/trap_ghost_0.jsonl +0 -0
  26. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/demo_fixtures/trap_piped_0.jsonl +0 -0
  27. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/feedback.py +0 -0
  28. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/hooks.py +0 -0
  29. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/judge.py +0 -0
  30. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/ledger.py +0 -0
  31. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/models.py +0 -0
  32. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/parsers.py +0 -0
  33. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/report.py +0 -0
  34. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/rerun.py +0 -0
  35. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/review.py +0 -0
  36. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/rules.py +0 -0
  37. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/twopass.py +0 -0
  38. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/verdicts.py +0 -0
  39. {custos_code-0.0.2 → custos_code-0.0.4}/src/custos_code/watchdog.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: custos-code
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: Checks a coding agent's final report against the log of what it actually did.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -64,6 +64,18 @@ did, what it said, the receipt, and the deterministic nudge that goes back. `cus
64
64
  the claims, the ledger they cite, and what the check cost, with no JavaScript in it.
65
65
  `--format markdown` writes what the PR bot posts. Both are options on `check`, not `demo`.
66
66
 
67
+ ## Architecture Scope
68
+
69
+ `custos-code arch` reads Mermaid flowcharts in repo docs and reports when touched files span
70
+ components with no declared edge:
71
+
72
+ ```bash
73
+ custos-code arch --repo . --touched "$(git diff --name-only | paste -sd, -)"
74
+ ```
75
+
76
+ The diagram should live in a Markdown file such as `docs/ARCHITECTURE.md`, and component names
77
+ should roughly match folders or files, for example `Billing` -> `src/billing/`.
78
+
67
79
  ## Prototype
68
80
 
69
81
  `docs/prototype/index.html` is an interactive, non-functional mock of the editor experience: marks on the agent's message, the evidence panel, editor decorations, the auto-mode loop, and the PR receipt. It is a single self-contained file:
@@ -38,6 +38,18 @@ did, what it said, the receipt, and the deterministic nudge that goes back. `cus
38
38
  the claims, the ledger they cite, and what the check cost, with no JavaScript in it.
39
39
  `--format markdown` writes what the PR bot posts. Both are options on `check`, not `demo`.
40
40
 
41
+ ## Architecture Scope
42
+
43
+ `custos-code arch` reads Mermaid flowcharts in repo docs and reports when touched files span
44
+ components with no declared edge:
45
+
46
+ ```bash
47
+ custos-code arch --repo . --touched "$(git diff --name-only | paste -sd, -)"
48
+ ```
49
+
50
+ The diagram should live in a Markdown file such as `docs/ARCHITECTURE.md`, and component names
51
+ should roughly match folders or files, for example `Billing` -> `src/billing/`.
52
+
41
53
  ## Prototype
42
54
 
43
55
  `docs/prototype/index.html` is an interactive, non-functional mock of the editor experience: marks on the agent's message, the evidence panel, editor decorations, the auto-mode loop, and the PR receipt. It is a single self-contained file:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "custos-code"
3
- version = "0.0.2"
3
+ version = "0.0.4"
4
4
  description = "Checks a coding agent's final report against the log of what it actually did."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -3,4 +3,4 @@
3
3
  Read AGENTS.md before changing anything. Invariants live there. Every module below has a
4
4
  docstring saying what it owns and what it must never do.
5
5
  """
6
- __version__ = "0.0.1"
6
+ __version__ = "0.0.4"
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
  import pathlib
12
12
  import re
13
13
  from dataclasses import dataclass
14
+ from importlib.metadata import PackageNotFoundError, version
14
15
 
15
16
  import typer
16
17
  from rich.console import Console
@@ -42,6 +43,29 @@ app = typer.Typer(
42
43
  console = Console()
43
44
 
44
45
 
46
+ def _print_version(show: bool) -> None:
47
+ if not show:
48
+ return
49
+ # Read from the installed package's own metadata, not a literal here, so this can never say
50
+ # something other than what was actually installed -- a hardcoded string drifts the moment a
51
+ # release changes pyproject.toml without someone remembering this one too.
52
+ try:
53
+ console.print(f"custos-code {version('custos-code')}")
54
+ except PackageNotFoundError:
55
+ console.print("custos-code (unknown version: not installed as a package)")
56
+ raise typer.Exit()
57
+
58
+
59
+ @app.callback()
60
+ def _main(
61
+ version_: bool = typer.Option(
62
+ False, "--version", "-V", callback=_print_version, is_eager=True,
63
+ help="Print the installed version and exit.",
64
+ ),
65
+ ) -> None:
66
+ return
67
+
68
+
45
69
  @app.command()
46
70
  def check(
47
71
  session: str | None = typer.Argument(
@@ -310,7 +310,7 @@ _FILE_WRITERS = frozenset({"tee", "touch", "cp", "mv", "dd", "install", "ln", "t
310
310
  _PREFIX_TOKENS = frozenset({"cd", "time", "timeout", "command", "exec", "nohup", "nice", "env"})
311
311
  _ASSIGN = re.compile(r"(?:^|[;&|]|\n)\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=([^\s;&|<>]+)")
312
312
  _VARREF = re.compile(r"\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?")
313
- _CD = re.compile(r"(?:^|[;&|]|\n)\s*cd\s+(?:--\s+)?([^\s;&|<>]+)")
313
+ _CD = re.compile(r"""(?:^|[;&|]|\n)\s*cd\s+(?:--\s+)?(?:"([^"]*)"|'([^']*)'|([^\s;&|<>]+))""")
314
314
  _SHELLS = frozenset({"bash", "sh", "zsh", "dash", "ksh", "eval", "xargs", "source", ".",
315
315
  "ssh", "env", "nohup", "timeout", "watch", "script"})
316
316
 
@@ -442,10 +442,18 @@ def _effective_cwd(cmd: str, cwd: str) -> str:
442
442
  Resolving against the session root invents a path that does not exist -- most of the
443
443
  `unrecoverable-write` volume -- and in the other direction MISSES the real target when the
444
444
  `cd` leaves the grant. An unexpandable `$VAR` aborts the walk rather than guessing.
445
+
446
+ `_CD` has three alternative groups -- double-quoted, single-quoted, bare -- because a bare
447
+ `[^\\s...]+` capture stops at the first space, and a path with a space in it is not a rare
448
+ edge case: `cd "/Users/x/Documents/HackMIT 2026/receipts" && ...` is what every compound
449
+ command in *this project's own checkout* looks like. Truncating the quoted target at that
450
+ space silently rebased every subsequent path in the command onto a directory one level up
451
+ that happens not to exist, which made `write-outside-cwd` fire on commands that never left
452
+ the grant at all.
445
453
  """
446
454
  d = cwd
447
455
  for m in _CD.finditer(_strip_heredocs(cmd)):
448
- tgt = m.group(1).strip("'\"")
456
+ tgt = next(g for g in m.groups() if g is not None)
449
457
  if "$" in tgt or "`" in tgt:
450
458
  return d
451
459
  d = _abs(tgt, d)
File without changes
File without changes