verkit 0.2.1__tar.gz → 0.3.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.
- verkit-0.3.0/AGENTS.md +21 -0
- verkit-0.3.0/CLAUDE.md +1 -0
- {verkit-0.2.1 → verkit-0.3.0}/PKG-INFO +16 -1
- {verkit-0.2.1 → verkit-0.3.0}/README.md +15 -0
- verkit-0.3.0/llms.txt +15 -0
- {verkit-0.2.1 → verkit-0.3.0}/pyproject.toml +1 -1
- verkit-0.3.0/src/verkit/agent.py +75 -0
- verkit-0.3.0/src/verkit/agent_guide.md +63 -0
- {verkit-0.2.1 → verkit-0.3.0}/src/verkit/cli.py +17 -1
- verkit-0.3.0/src/verkit/keys.py +29 -0
- verkit-0.3.0/tests/test_agent.py +30 -0
- {verkit-0.2.1 → verkit-0.3.0}/uv.lock +1 -1
- {verkit-0.2.1 → verkit-0.3.0}/.github/workflows/ci.yml +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/.github/workflows/publish.yml +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/.gitignore +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/src/verkit/__init__.py +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/src/verkit/inspector.py +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/src/verkit/promoter.py +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/src/verkit/py.typed +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/src/verkit/tagger.py +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/src/verkit/theme.json +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/src/verkit/theme.py +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/tests/test_inspector.py +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/tests/test_promoter.py +0 -0
- {verkit-0.2.1 → verkit-0.3.0}/tests/test_theme.py +0 -0
verkit-0.3.0/AGENTS.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
verkit: polyglot version inspector/promoter/tagger, plus shared Rich theme and
|
|
4
|
+
Textual key conventions used by the author's other TUIs.
|
|
5
|
+
|
|
6
|
+
## Commands
|
|
7
|
+
- Setup: `uv sync`
|
|
8
|
+
- Test: `uv run pytest`
|
|
9
|
+
- Lint: `uv run ruff check .`
|
|
10
|
+
- Release: `verkit release <major|minor|patch>` (do not hand-edit versions)
|
|
11
|
+
|
|
12
|
+
## Layout
|
|
13
|
+
- `src/verkit/agent_guide.md`: canonical agent conventions (shipped in wheel;
|
|
14
|
+
printed by `verkit conventions`). Edit here, not in consumers.
|
|
15
|
+
- `src/verkit/keys.py`: `VIM_BINDINGS`. `src/verkit/theme.py` + `theme.json`: theme
|
|
16
|
+
(kept in sync by `tests/test_theme.py`).
|
|
17
|
+
- `src/verkit/agent.py`: `conventions` / `init-agent` implementation.
|
|
18
|
+
|
|
19
|
+
## Rules
|
|
20
|
+
- No Textual import in verkit; keep `keys.py` pure data.
|
|
21
|
+
- Changing a convention means updating `agent_guide.md`, `llms.txt`, README, and tests.
|
verkit-0.3.0/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: verkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Polyglot project version inspector, promoter, tagger, and release workflow engine
|
|
5
5
|
Author: Mark Stouffer
|
|
6
6
|
License: MIT
|
|
@@ -122,3 +122,18 @@ $theme = Invoke-RestMethod `
|
|
|
122
122
|
# $theme.theme.header_background_ansi -> ANSI 256 index 237 background escape
|
|
123
123
|
# $theme.theme.header_background_rgb_hex -> "#3a3a3a"
|
|
124
124
|
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## For AI Agents
|
|
129
|
+
|
|
130
|
+
Projects that depend on `verkit` should follow its conventions: vim-style
|
|
131
|
+
navigation (`from verkit.keys import VIM_BINDINGS`), the shared theme, and a
|
|
132
|
+
`mise`-only local dev override (PyPI release everywhere else).
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
verkit conventions # print the guide (add --json for structured output)
|
|
136
|
+
verkit init-agent --mise # add pointer to AGENTS.md and a mise.toml template
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
See also `AGENTS.md` and `llms.txt` in this repo.
|
|
@@ -112,3 +112,18 @@ $theme = Invoke-RestMethod `
|
|
|
112
112
|
# $theme.theme.header_background_ansi -> ANSI 256 index 237 background escape
|
|
113
113
|
# $theme.theme.header_background_rgb_hex -> "#3a3a3a"
|
|
114
114
|
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## For AI Agents
|
|
119
|
+
|
|
120
|
+
Projects that depend on `verkit` should follow its conventions: vim-style
|
|
121
|
+
navigation (`from verkit.keys import VIM_BINDINGS`), the shared theme, and a
|
|
122
|
+
`mise`-only local dev override (PyPI release everywhere else).
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
verkit conventions # print the guide (add --json for structured output)
|
|
126
|
+
verkit init-agent --mise # add pointer to AGENTS.md and a mise.toml template
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
See also `AGENTS.md` and `llms.txt` in this repo.
|
verkit-0.3.0/llms.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# verkit
|
|
2
|
+
|
|
3
|
+
> Polyglot project version inspector, promoter, tagger and release CLI, plus the shared Rich theme and vim-style Textual key conventions for the author's Python TUIs. If your project depends on verkit, read the conventions guide first.
|
|
4
|
+
|
|
5
|
+
## Agent guidance
|
|
6
|
+
- [Conventions guide](https://raw.githubusercontent.com/bizkite-co/verkit/main/src/verkit/agent_guide.md): vim navigation, theme, mise dev-vs-PyPI rule. Also `verkit conventions [--json]`.
|
|
7
|
+
- [AGENTS.md](https://raw.githubusercontent.com/bizkite-co/verkit/main/AGENTS.md): working on verkit itself.
|
|
8
|
+
|
|
9
|
+
## Code
|
|
10
|
+
- [keys.py](https://raw.githubusercontent.com/bizkite-co/verkit/main/src/verkit/keys.py): `VIM_BINDINGS` for Textual `BINDINGS`.
|
|
11
|
+
- [theme.py](https://raw.githubusercontent.com/bizkite-co/verkit/main/src/verkit/theme.py): shared Rich theme.
|
|
12
|
+
- [theme.json](https://raw.githubusercontent.com/bizkite-co/verkit/main/src/verkit/theme.json): theme for non-Python consumers.
|
|
13
|
+
|
|
14
|
+
## Optional
|
|
15
|
+
- [README](https://raw.githubusercontent.com/bizkite-co/verkit/main/README.md): CLI and API usage.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Agent-facing commands: print conventions and wire them into a project."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from importlib import resources
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from verkit.keys import VIM_BINDINGS
|
|
10
|
+
|
|
11
|
+
MARKER = "<!-- verkit:agent-guide -->"
|
|
12
|
+
|
|
13
|
+
SNIPPET = f"""{MARKER}
|
|
14
|
+
## verkit conventions
|
|
15
|
+
|
|
16
|
+
This project depends on `verkit`. Before touching TUI, theme, or dev-setup
|
|
17
|
+
code, run `verkit conventions` (or read `verkit/agent_guide.md` in the
|
|
18
|
+
installed package). Key rules: vim-style navigation via
|
|
19
|
+
`verkit.keys.VIM_BINDINGS`, shared theme via `verkit.theme`, and local verkit
|
|
20
|
+
dev override through `mise` only (never in `pyproject.toml`).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
MISE_TEMPLATE = """# Generated by `verkit init-agent --mise`.
|
|
24
|
+
# Local checkout of verkit is used when it exists; CI/Docker/users get PyPI.
|
|
25
|
+
[env]
|
|
26
|
+
VERKIT_DEV = "{{ env.HOME }}/repos/verkit"
|
|
27
|
+
UV_NO_SYNC = "1" # keep uv from replacing the editable install with PyPI's
|
|
28
|
+
|
|
29
|
+
[tasks.install]
|
|
30
|
+
run = '''
|
|
31
|
+
uv sync
|
|
32
|
+
if [ -d "$VERKIT_DEV" ]; then uv pip install -e "$VERKIT_DEV"; fi
|
|
33
|
+
'''
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def guide_text() -> str:
|
|
38
|
+
return resources.files("verkit").joinpath("agent_guide.md").read_text(encoding="utf-8")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def conventions_json() -> str:
|
|
42
|
+
return json.dumps(
|
|
43
|
+
{
|
|
44
|
+
"vim_bindings": [
|
|
45
|
+
{"key": k, "action": a, "description": d} for k, a, d in VIM_BINDINGS
|
|
46
|
+
],
|
|
47
|
+
"import": "from verkit.keys import VIM_BINDINGS",
|
|
48
|
+
"theme_import": "from verkit.theme import DEFAULT as theme",
|
|
49
|
+
"dev_override": "mise.toml only; never pyproject.toml",
|
|
50
|
+
"guide": guide_text(),
|
|
51
|
+
},
|
|
52
|
+
indent=2,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def init_agent(directory: Path, mise: bool = False) -> list[str]:
|
|
57
|
+
"""Idempotently add the pointer to AGENTS.md (and mise.toml if asked)."""
|
|
58
|
+
changed: list[str] = []
|
|
59
|
+
agents = directory / "AGENTS.md"
|
|
60
|
+
existing = agents.read_text(encoding="utf-8") if agents.exists() else ""
|
|
61
|
+
if MARKER not in existing:
|
|
62
|
+
sep = "\n" if existing and not existing.endswith("\n\n") else ""
|
|
63
|
+
agents.write_text(existing + sep + SNIPPET, encoding="utf-8")
|
|
64
|
+
changed.append(str(agents))
|
|
65
|
+
if mise:
|
|
66
|
+
target = directory / "mise.toml"
|
|
67
|
+
present = [f for f in (target, directory / "mise.verkit.toml") if f.exists()]
|
|
68
|
+
if any("VERKIT_DEV" in f.read_text(encoding="utf-8") for f in present):
|
|
69
|
+
return changed
|
|
70
|
+
if target.exists():
|
|
71
|
+
target = directory / "mise.verkit.toml" # never clobber; merge by hand
|
|
72
|
+
if not target.exists():
|
|
73
|
+
target.write_text(MISE_TEMPLATE, encoding="utf-8")
|
|
74
|
+
changed.append(str(target))
|
|
75
|
+
return changed
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# verkit conventions for AI agents
|
|
2
|
+
|
|
3
|
+
verkit is the shared foundation for this developer's Python Textual/Rich TUIs
|
|
4
|
+
and CLIs. If the project you are working in depends on `verkit`, follow these
|
|
5
|
+
conventions. Print this guide any time with `verkit conventions`.
|
|
6
|
+
|
|
7
|
+
## 1. Vim-style navigation (required for every TUI)
|
|
8
|
+
|
|
9
|
+
Every Textual TUI must support vim-style navigation **in addition to** the
|
|
10
|
+
arrow keys, never instead of them.
|
|
11
|
+
|
|
12
|
+
| Key | Action | Textual action |
|
|
13
|
+
| :-- | :-- | :-- |
|
|
14
|
+
| `j` / `k` | move down / up | `cursor_down` / `cursor_up` |
|
|
15
|
+
| `h` / `l` | move left / right | `cursor_left` / `cursor_right` |
|
|
16
|
+
| `g` / `G` | jump to top / bottom | `scroll_home` / `scroll_end` |
|
|
17
|
+
| `ctrl+d` / `ctrl+u` | half-page down / up | `page_down` / `page_up` |
|
|
18
|
+
| `/` | search / filter | app-specific |
|
|
19
|
+
| `escape` | go back / close | app-specific |
|
|
20
|
+
| `q` | quit | `quit` |
|
|
21
|
+
|
|
22
|
+
Do not hand-write these. Import them:
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from verkit.keys import VIM_BINDINGS
|
|
26
|
+
|
|
27
|
+
class MyApp(App):
|
|
28
|
+
BINDINGS = [*VIM_BINDINGS, ("f", "filter", "Filter")]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Show bindings in the `Footer`. Do not reassign these keys to other meanings.
|
|
32
|
+
|
|
33
|
+
## 2. Theme
|
|
34
|
+
|
|
35
|
+
Use the shared Rich theme; never copy-paste style constants:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from verkit.theme import DEFAULT as theme
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Machine-readable mirror for non-Python consumers: `theme.json` in the package.
|
|
42
|
+
|
|
43
|
+
## 3. Dev vs. released verkit (mise)
|
|
44
|
+
|
|
45
|
+
Rule: **in the local checkout, run the local verkit source; everywhere else
|
|
46
|
+
(CI, Docker, end users) install the latest release from PyPI.**
|
|
47
|
+
|
|
48
|
+
- `pyproject.toml` declares a plain dependency (`"verkit"`). Never add a
|
|
49
|
+
`path`/`editable` source there: it would leak to CI and to PyPI consumers.
|
|
50
|
+
- Local override lives only in the consumer's `mise.toml`, and is a no-op when
|
|
51
|
+
the local verkit checkout does not exist. Generate it with
|
|
52
|
+
`verkit init-agent --mise`. It sets `VERKIT_DEV` (default
|
|
53
|
+
`~/repos/verkit`) and installs it editable after `uv sync`.
|
|
54
|
+
- Use `mise run install` (not bare `uv sync`), and run tools with
|
|
55
|
+
`UV_NO_SYNC=1` (set by the mise template) so uv does not replace the
|
|
56
|
+
editable install with the PyPI release.
|
|
57
|
+
|
|
58
|
+
## 4. Agent-friendly CLI
|
|
59
|
+
|
|
60
|
+
- `verkit conventions [--json]` prints this guide (or structured JSON).
|
|
61
|
+
- `verkit init-agent [--mise] [--dir PATH]` adds a pointer to this guide in the
|
|
62
|
+
project's `AGENTS.md` (idempotent) and, with `--mise`, writes the mise template.
|
|
63
|
+
- Errors exit non-zero, so agents can rely on exit codes.
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import sys
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
from rich.console import Console
|
|
5
6
|
|
|
7
|
+
from verkit.agent import conventions_json, guide_text, init_agent
|
|
6
8
|
from verkit.inspector import display_version_info
|
|
7
9
|
from verkit.promoter import promote_version
|
|
8
10
|
from verkit.tagger import release_version, tag_version
|
|
@@ -47,6 +49,15 @@ def main():
|
|
|
47
49
|
help="Promote and tag locally only",
|
|
48
50
|
)
|
|
49
51
|
|
|
52
|
+
# verkit conventions [--json]
|
|
53
|
+
conv_p = subparsers.add_parser("conventions", help="Print conventions for AI agents")
|
|
54
|
+
conv_p.add_argument("--json", action="store_true", help="Structured output")
|
|
55
|
+
|
|
56
|
+
# verkit init-agent
|
|
57
|
+
init_p = subparsers.add_parser("init-agent", help="Add verkit guidance to a project")
|
|
58
|
+
init_p.add_argument("--dir", default=".", help="Project directory")
|
|
59
|
+
init_p.add_argument("--mise", action="store_true", help="Also write mise.toml template")
|
|
60
|
+
|
|
50
61
|
args = parser.parse_args()
|
|
51
62
|
console = Console()
|
|
52
63
|
|
|
@@ -55,7 +66,12 @@ def main():
|
|
|
55
66
|
return
|
|
56
67
|
|
|
57
68
|
try:
|
|
58
|
-
if args.command == "
|
|
69
|
+
if args.command == "conventions":
|
|
70
|
+
print(conventions_json() if args.json else guide_text())
|
|
71
|
+
elif args.command == "init-agent":
|
|
72
|
+
changed = init_agent(Path(args.dir), mise=args.mise)
|
|
73
|
+
console.print("\n".join(f"updated {c}" for c in changed) or "already up to date")
|
|
74
|
+
elif args.command == "promote":
|
|
59
75
|
promote_version(args.part, console=console, allow_amend=getattr(args, "allow_amend", True))
|
|
60
76
|
elif args.command == "tag":
|
|
61
77
|
tag_version(console=console, push=getattr(args, "push", True))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Canonical vim-style key bindings for Textual TUIs.
|
|
2
|
+
|
|
3
|
+
Pure data with no Textual import, so verkit does not depend on Textual.
|
|
4
|
+
Entries are ``(key, action, description)`` tuples, which is what Textual's
|
|
5
|
+
``BINDINGS`` accepts directly::
|
|
6
|
+
|
|
7
|
+
from verkit.keys import VIM_BINDINGS
|
|
8
|
+
|
|
9
|
+
class MyApp(App):
|
|
10
|
+
BINDINGS = [*VIM_BINDINGS, ("f", "filter", "Filter")]
|
|
11
|
+
|
|
12
|
+
Arrow keys keep working through Textual's defaults; these add the vim keys.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
VIM_BINDINGS: list[tuple[str, str, str]] = [
|
|
18
|
+
("j", "cursor_down", "Down"),
|
|
19
|
+
("k", "cursor_up", "Up"),
|
|
20
|
+
("h", "cursor_left", "Left"),
|
|
21
|
+
("l", "cursor_right", "Right"),
|
|
22
|
+
("g", "scroll_home", "Top"),
|
|
23
|
+
("G", "scroll_end", "Bottom"),
|
|
24
|
+
("ctrl+d", "page_down", "Page down"),
|
|
25
|
+
("ctrl+u", "page_up", "Page up"),
|
|
26
|
+
("q", "quit", "Quit"),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
__all__ = ["VIM_BINDINGS"]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from verkit.agent import MARKER, conventions_json, guide_text, init_agent
|
|
4
|
+
from verkit.keys import VIM_BINDINGS
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_guide_mentions_bindings_and_mise():
|
|
8
|
+
g = guide_text()
|
|
9
|
+
assert "VIM_BINDINGS" in g and "mise" in g
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_bindings_shape_and_vim_keys():
|
|
13
|
+
assert all(len(b) == 3 for b in VIM_BINDINGS)
|
|
14
|
+
assert {"j", "k", "h", "l"} <= {b[0] for b in VIM_BINDINGS}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_json_output():
|
|
18
|
+
data = json.loads(conventions_json())
|
|
19
|
+
assert data["vim_bindings"][0]["key"] == "j"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_init_agent_idempotent_and_no_clobber(tmp_path):
|
|
23
|
+
(tmp_path / "AGENTS.md").write_text("# Mine\n")
|
|
24
|
+
assert init_agent(tmp_path, mise=True)
|
|
25
|
+
assert init_agent(tmp_path, mise=True) == []
|
|
26
|
+
text = (tmp_path / "AGENTS.md").read_text()
|
|
27
|
+
assert text.startswith("# Mine") and text.count(MARKER) == 1
|
|
28
|
+
(tmp_path / "mise.toml").write_text("[tools]\n")
|
|
29
|
+
init_agent(tmp_path, mise=True)
|
|
30
|
+
assert (tmp_path / "mise.toml").read_text() == "[tools]\n"
|
|
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
|