agent-sdlc 0.3.0__tar.gz → 0.3.1__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.
- {agent_sdlc-0.3.0/agent_sdlc.egg-info → agent_sdlc-0.3.1}/PKG-INFO +20 -8
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/README.md +19 -7
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1/agent_sdlc.egg-info}/PKG-INFO +20 -8
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/__init__.py +1 -1
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/no_leaks.py +5 -1
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/runner.py +8 -2
- agent_sdlc-0.3.1/tests/test_say_narrate_bin.py +35 -0
- agent_sdlc-0.3.0/tests/test_say_narrate_bin.py +0 -26
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/LICENSE +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/NOTICE.md +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/agent_sdlc.egg-info/SOURCES.txt +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/agent_sdlc.egg-info/dependency_links.txt +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/agent_sdlc.egg-info/entry_points.txt +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/agent_sdlc.egg-info/requires.txt +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/agent_sdlc.egg-info/top_level.txt +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/adversary.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/cli.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/commit_msg.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/coverage_map.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/diff_discipline.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/model_vv.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/mutants.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/no_comments.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/no_new_docs.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/repo.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/rules.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/token.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/vocabulary.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/vocabulary_check.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/vocabulary_core.toml +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/vocabulary_molds.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/vocabulary_path.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/vocabulary_wordnet.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/mutation_gate/waivers.py +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/pyproject.toml +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/rules/code-clarity.md +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/rules/diff-discipline.md +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/rules/model-vv.md +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/rules/naming.md +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/rules/testing.md +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/rules/tickets.md +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/rules/voice.md +0 -0
- {agent_sdlc-0.3.0 → agent_sdlc-0.3.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-sdlc
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Diff-scoped mutation gate for pre-commit and agent Stop hooks
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/PavelGuzenfeld/agent-sdlc
|
|
@@ -62,22 +62,34 @@ Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
|
|
|
62
62
|
|
|
63
63
|
## Install
|
|
64
64
|
|
|
65
|
+
Claude Code:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
claude plugin marketplace add PavelGuzenfeld/agent-sdlc
|
|
69
|
+
claude plugin install agent-sdlc@agent-sdlc
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Tools the gate needs, and Codex:
|
|
73
|
+
|
|
65
74
|
```bash
|
|
66
75
|
git clone https://github.com/PavelGuzenfeld/agent-sdlc
|
|
67
76
|
cd agent-sdlc
|
|
68
|
-
./install.sh --
|
|
77
|
+
./install.sh --deps
|
|
69
78
|
```
|
|
70
79
|
|
|
71
80
|
| Want | Do |
|
|
72
81
|
|---|---|
|
|
73
|
-
|
|
|
82
|
+
| Claude Code | The plugin above; `--scope local` or `--scope project` keeps it to one repo |
|
|
83
|
+
| Codex | `./install.sh --target codex`, or add the repo as a Codex marketplace source |
|
|
84
|
+
| Gate dependencies | `./install.sh --deps` (`--deps=say` adds voice) |
|
|
74
85
|
| Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
|
|
75
|
-
| A plugin | `claude --plugin-dir /path/to/agent-sdlc`, or add the repo as a Codex marketplace source |
|
|
76
86
|
| The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
|
|
77
87
|
|
|
78
|
-
- `install.sh` links `skills/` into `~/.
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
- `install.sh` links `skills/` into `~/.codex` and renders `commands/` as
|
|
89
|
+
Codex skills. It installs nothing into `~/.claude`. A second run changes
|
|
90
|
+
nothing.
|
|
91
|
+
- Installed an older `install.sh` into `~/.claude`? `./install.sh
|
|
92
|
+
--uninstall-legacy` removes those links and hooks, or every hook fires twice.
|
|
81
93
|
- `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
|
|
82
94
|
`ast-grep`.
|
|
83
95
|
|
|
@@ -85,7 +97,7 @@ cd agent-sdlc
|
|
|
85
97
|
|
|
86
98
|
In Claude Code (Codex: `$name` instead of `/name`):
|
|
87
99
|
|
|
88
|
-
1. `./install.sh --
|
|
100
|
+
1. Install the plugin and `./install.sh --deps`
|
|
89
101
|
2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
|
|
90
102
|
3. `claude`, then `/grill <idea>` or `gh issue create …`
|
|
91
103
|
4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
|
|
@@ -47,22 +47,34 @@ Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
|
|
|
47
47
|
|
|
48
48
|
## Install
|
|
49
49
|
|
|
50
|
+
Claude Code:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
claude plugin marketplace add PavelGuzenfeld/agent-sdlc
|
|
54
|
+
claude plugin install agent-sdlc@agent-sdlc
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Tools the gate needs, and Codex:
|
|
58
|
+
|
|
50
59
|
```bash
|
|
51
60
|
git clone https://github.com/PavelGuzenfeld/agent-sdlc
|
|
52
61
|
cd agent-sdlc
|
|
53
|
-
./install.sh --
|
|
62
|
+
./install.sh --deps
|
|
54
63
|
```
|
|
55
64
|
|
|
56
65
|
| Want | Do |
|
|
57
66
|
|---|---|
|
|
58
|
-
|
|
|
67
|
+
| Claude Code | The plugin above; `--scope local` or `--scope project` keeps it to one repo |
|
|
68
|
+
| Codex | `./install.sh --target codex`, or add the repo as a Codex marketplace source |
|
|
69
|
+
| Gate dependencies | `./install.sh --deps` (`--deps=say` adds voice) |
|
|
59
70
|
| Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
|
|
60
|
-
| A plugin | `claude --plugin-dir /path/to/agent-sdlc`, or add the repo as a Codex marketplace source |
|
|
61
71
|
| The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
|
|
62
72
|
|
|
63
|
-
- `install.sh` links `skills/` into `~/.
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
- `install.sh` links `skills/` into `~/.codex` and renders `commands/` as
|
|
74
|
+
Codex skills. It installs nothing into `~/.claude`. A second run changes
|
|
75
|
+
nothing.
|
|
76
|
+
- Installed an older `install.sh` into `~/.claude`? `./install.sh
|
|
77
|
+
--uninstall-legacy` removes those links and hooks, or every hook fires twice.
|
|
66
78
|
- `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
|
|
67
79
|
`ast-grep`.
|
|
68
80
|
|
|
@@ -70,7 +82,7 @@ cd agent-sdlc
|
|
|
70
82
|
|
|
71
83
|
In Claude Code (Codex: `$name` instead of `/name`):
|
|
72
84
|
|
|
73
|
-
1. `./install.sh --
|
|
85
|
+
1. Install the plugin and `./install.sh --deps`
|
|
74
86
|
2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
|
|
75
87
|
3. `claude`, then `/grill <idea>` or `gh issue create …`
|
|
76
88
|
4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-sdlc
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Diff-scoped mutation gate for pre-commit and agent Stop hooks
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/PavelGuzenfeld/agent-sdlc
|
|
@@ -62,22 +62,34 @@ Add `assert is_adult(18)` and `assert not is_adult(17)`, and the gate passes.
|
|
|
62
62
|
|
|
63
63
|
## Install
|
|
64
64
|
|
|
65
|
+
Claude Code:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
claude plugin marketplace add PavelGuzenfeld/agent-sdlc
|
|
69
|
+
claude plugin install agent-sdlc@agent-sdlc
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Tools the gate needs, and Codex:
|
|
73
|
+
|
|
65
74
|
```bash
|
|
66
75
|
git clone https://github.com/PavelGuzenfeld/agent-sdlc
|
|
67
76
|
cd agent-sdlc
|
|
68
|
-
./install.sh --
|
|
77
|
+
./install.sh --deps
|
|
69
78
|
```
|
|
70
79
|
|
|
71
80
|
| Want | Do |
|
|
72
81
|
|---|---|
|
|
73
|
-
|
|
|
82
|
+
| Claude Code | The plugin above; `--scope local` or `--scope project` keeps it to one repo |
|
|
83
|
+
| Codex | `./install.sh --target codex`, or add the repo as a Codex marketplace source |
|
|
84
|
+
| Gate dependencies | `./install.sh --deps` (`--deps=say` adds voice) |
|
|
74
85
|
| Only the gate | `pip install agent-sdlc` (WordNet check: `agent-sdlc[vocabulary]`) |
|
|
75
|
-
| A plugin | `claude --plugin-dir /path/to/agent-sdlc`, or add the repo as a Codex marketplace source |
|
|
76
86
|
| The gate on a repo | Add `.mutation-gate.toml`, wire the pre-commit hooks |
|
|
77
87
|
|
|
78
|
-
- `install.sh` links `skills/` into `~/.
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
- `install.sh` links `skills/` into `~/.codex` and renders `commands/` as
|
|
89
|
+
Codex skills. It installs nothing into `~/.claude`. A second run changes
|
|
90
|
+
nothing.
|
|
91
|
+
- Installed an older `install.sh` into `~/.claude`? `./install.sh
|
|
92
|
+
--uninstall-legacy` removes those links and hooks, or every hook fires twice.
|
|
81
93
|
- `ast-grep-cli` adds an `sg` shim that can shadow the system `sg`; call
|
|
82
94
|
`ast-grep`.
|
|
83
95
|
|
|
@@ -85,7 +97,7 @@ cd agent-sdlc
|
|
|
85
97
|
|
|
86
98
|
In Claude Code (Codex: `$name` instead of `/name`):
|
|
87
99
|
|
|
88
|
-
1. `./install.sh --
|
|
100
|
+
1. Install the plugin and `./install.sh --deps`
|
|
89
101
|
2. In your repo: `touch .mutation-gate.toml && mutation-gate rules sync`
|
|
90
102
|
3. `claude`, then `/grill <idea>` or `gh issue create …`
|
|
91
103
|
4. Approve the ticket: `gh issue edit 42 --add-label model:sonnet`
|
|
@@ -39,6 +39,9 @@ _GIT_SSH_CLONE_RE = re.compile(
|
|
|
39
39
|
_NPM_VERSION_RE = re.compile(
|
|
40
40
|
r"(^|[^A-Za-z0-9._%+/-])[A-Za-z0-9._%+-]+@[0-9]+\.[0-9]+\.[0-9]+([^0-9.]|$)"
|
|
41
41
|
)
|
|
42
|
+
_CLAUDE_PLUGIN_ID_RE = re.compile(
|
|
43
|
+
r"(^|[^A-Za-z0-9._%+/-])[A-Za-z0-9._-]+@[a]gent-sdlc([^A-Za-z0-9.-]|$)"
|
|
44
|
+
)
|
|
42
45
|
|
|
43
46
|
_BULLET_RE = re.compile(r"^\s*-\s*")
|
|
44
47
|
_ARROW = "→"
|
|
@@ -54,7 +57,8 @@ class BannedName:
|
|
|
54
57
|
|
|
55
58
|
def _carve_out(line: str) -> str:
|
|
56
59
|
line = _GIT_SSH_CLONE_RE.sub(r"\1public-git-ssh-clone-url", line)
|
|
57
|
-
|
|
60
|
+
line = _NPM_VERSION_RE.sub(r"\1npm-package-version\2", line)
|
|
61
|
+
return _CLAUDE_PLUGIN_ID_RE.sub(r"\1claude-plugin-id\2", line)
|
|
58
62
|
|
|
59
63
|
|
|
60
64
|
def generic_hit(line: str) -> bool:
|
|
@@ -45,18 +45,24 @@ TERM_GRACE_SECONDS = 15.0
|
|
|
45
45
|
|
|
46
46
|
DOCKER_RUN_RE = re.compile(r"\bdocker\s+run\b")
|
|
47
47
|
|
|
48
|
+
UNWRITTEN_PYCACHE = "/nonexistent/mutation-gate-pycache"
|
|
49
|
+
|
|
48
50
|
# Root in the container writes these into the bind-mounted repo as directories a
|
|
49
51
|
# host user then cannot rm without starting another container (#74).
|
|
50
|
-
NO_ROOT_CACHE =
|
|
52
|
+
NO_ROOT_CACHE = (
|
|
53
|
+
f"-e PYTHONDONTWRITEBYTECODE=1 -e PYTHONPYCACHEPREFIX={UNWRITTEN_PYCACHE}"
|
|
54
|
+
" -e PYTEST_ADDOPTS='-p no:cacheprovider'"
|
|
55
|
+
)
|
|
51
56
|
|
|
52
57
|
GIT_REPO_SCOPED = ("GIT_DIR", "GIT_INDEX_FILE", "GIT_WORK_TREE")
|
|
58
|
+
NO_BYTECODE = {"PYTHONDONTWRITEBYTECODE": "1", "PYTHONPYCACHEPREFIX": UNWRITTEN_PYCACHE}
|
|
53
59
|
|
|
54
60
|
|
|
55
61
|
def _test_env() -> dict[str, str]:
|
|
56
62
|
"""The environment a repo's own command runs in. Git exports these into every
|
|
57
63
|
hook and they override `-C`, so a suite that builds a temp repo stages into
|
|
58
64
|
the commit being gated instead (flowdiff#83)."""
|
|
59
|
-
return {k: v for k, v in os.environ.items() if k not in GIT_REPO_SCOPED}
|
|
65
|
+
return {k: v for k, v in os.environ.items() if k not in GIT_REPO_SCOPED} | NO_BYTECODE
|
|
60
66
|
|
|
61
67
|
|
|
62
68
|
@dataclass
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
MODULE_PATH = Path(__file__).resolve().parents[1] / "bin" / "say-narrate.py"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _load_from(path: Path):
|
|
9
|
+
spec = importlib.util.spec_from_file_location("say_narrate_probe", path)
|
|
10
|
+
module = importlib.util.module_from_spec(spec)
|
|
11
|
+
spec.loader.exec_module(module)
|
|
12
|
+
return module
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_bin_is_the_scripts_own_directory_under_a_plugin_only_install(monkeypatch, tmp_path):
|
|
16
|
+
monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False)
|
|
17
|
+
monkeypatch.setenv("HOME", str(tmp_path / "home-without-claude"))
|
|
18
|
+
plugin_bin = tmp_path / "plugin" / "bin"
|
|
19
|
+
plugin_bin.mkdir(parents=True)
|
|
20
|
+
shutil.copy(MODULE_PATH, plugin_bin / "say-narrate.py")
|
|
21
|
+
|
|
22
|
+
module = _load_from(plugin_bin / "say-narrate.py")
|
|
23
|
+
|
|
24
|
+
assert module.BIN == str(plugin_bin)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_bin_ignores_a_plugin_root_that_points_somewhere_else(monkeypatch, tmp_path):
|
|
28
|
+
monkeypatch.setenv("CLAUDE_PLUGIN_ROOT", "/fake/plugin/root")
|
|
29
|
+
legacy_bin = tmp_path / ".claude" / "bin"
|
|
30
|
+
legacy_bin.mkdir(parents=True)
|
|
31
|
+
(legacy_bin / "say-narrate.py").symlink_to(MODULE_PATH)
|
|
32
|
+
|
|
33
|
+
module = _load_from(legacy_bin / "say-narrate.py")
|
|
34
|
+
|
|
35
|
+
assert module.BIN == str(legacy_bin)
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import importlib.util
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
MODULE_PATH = Path(__file__).resolve().parents[1] / "bin" / "say-narrate.py"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def _load(monkeypatch, plugin_root):
|
|
8
|
-
if plugin_root is None:
|
|
9
|
-
monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False)
|
|
10
|
-
else:
|
|
11
|
-
monkeypatch.setenv("CLAUDE_PLUGIN_ROOT", plugin_root)
|
|
12
|
-
spec = importlib.util.spec_from_file_location("say_narrate_probe", MODULE_PATH)
|
|
13
|
-
module = importlib.util.module_from_spec(spec)
|
|
14
|
-
spec.loader.exec_module(module)
|
|
15
|
-
return module
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_bin_resolves_under_plugin_root_when_set(monkeypatch):
|
|
19
|
-
module = _load(monkeypatch, "/fake/plugin/root")
|
|
20
|
-
assert module.BIN == "/fake/plugin/root/bin"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def test_bin_falls_back_to_legacy_claude_bin_when_unset(monkeypatch, tmp_path):
|
|
24
|
-
monkeypatch.setenv("HOME", str(tmp_path))
|
|
25
|
-
module = _load(monkeypatch, None)
|
|
26
|
-
assert module.BIN == str(tmp_path / ".claude" / "bin")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|