verkit 0.1.5__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.1.5 → verkit-0.3.0}/PKG-INFO +52 -1
- {verkit-0.1.5 → verkit-0.3.0}/README.md +51 -0
- verkit-0.3.0/llms.txt +15 -0
- {verkit-0.1.5 → verkit-0.3.0}/pyproject.toml +1 -1
- {verkit-0.1.5 → verkit-0.3.0}/src/verkit/__init__.py +8 -5
- verkit-0.3.0/src/verkit/agent.py +75 -0
- verkit-0.3.0/src/verkit/agent_guide.md +63 -0
- {verkit-0.1.5 → verkit-0.3.0}/src/verkit/cli.py +17 -1
- verkit-0.3.0/src/verkit/keys.py +29 -0
- verkit-0.3.0/src/verkit/theme.json +18 -0
- verkit-0.3.0/src/verkit/theme.py +55 -0
- verkit-0.3.0/tests/test_agent.py +30 -0
- verkit-0.3.0/tests/test_theme.py +38 -0
- {verkit-0.1.5 → verkit-0.3.0}/uv.lock +1 -1
- {verkit-0.1.5 → verkit-0.3.0}/.github/workflows/ci.yml +0 -0
- {verkit-0.1.5 → verkit-0.3.0}/.github/workflows/publish.yml +0 -0
- {verkit-0.1.5 → verkit-0.3.0}/.gitignore +0 -0
- {verkit-0.1.5 → verkit-0.3.0}/src/verkit/inspector.py +0 -0
- {verkit-0.1.5 → verkit-0.3.0}/src/verkit/promoter.py +0 -0
- {verkit-0.1.5 → verkit-0.3.0}/src/verkit/py.typed +0 -0
- {verkit-0.1.5 → verkit-0.3.0}/src/verkit/tagger.py +0 -0
- {verkit-0.1.5 → verkit-0.3.0}/tests/test_inspector.py +0 -0
- {verkit-0.1.5 → verkit-0.3.0}/tests/test_promoter.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
|
|
@@ -86,3 +86,54 @@ tag_name = verkit.tag_version(push=True)
|
|
|
86
86
|
# 4. Atomic release
|
|
87
87
|
verkit.release_version("patch", push=True)
|
|
88
88
|
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Shared Terminal Theme
|
|
93
|
+
|
|
94
|
+
`verkit` publishes the canonical Rich theme used across this developer's
|
|
95
|
+
CLIs (`task-agent`, `multi-agent-registry`, ...). It is **opt-in** — verkit
|
|
96
|
+
never applies the theme for you; consumers import it and apply it to their
|
|
97
|
+
own `Table`/`Panel` objects:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from rich.console import Console
|
|
101
|
+
from rich.table import Table
|
|
102
|
+
from verkit.theme import DEFAULT as theme
|
|
103
|
+
|
|
104
|
+
console = Console()
|
|
105
|
+
table = Table(
|
|
106
|
+
title="Portfolio",
|
|
107
|
+
box=theme.table_box, # borderless
|
|
108
|
+
header_style=theme.header_style, # "on grey23"
|
|
109
|
+
padding=theme.table_padding, # (0, 2, 0, 0)
|
|
110
|
+
)
|
|
111
|
+
console.print(table)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The theme is a frozen dataclass (`verkit.theme.Theme`); `DEFAULT` is the
|
|
115
|
+
canonical instance. Non-Python consumers (e.g. PowerShell scripts) read the
|
|
116
|
+
machine-readable mirror, which stays in sync via `tests/test_theme.py`:
|
|
117
|
+
|
|
118
|
+
```powershell
|
|
119
|
+
$theme = Invoke-RestMethod `
|
|
120
|
+
https://raw.githubusercontent.com/bizkite-co/verkit/main/src/verkit/theme.json
|
|
121
|
+
# $theme.theme.header_style -> "on grey23"
|
|
122
|
+
# $theme.theme.header_background_ansi -> ANSI 256 index 237 background escape
|
|
123
|
+
# $theme.theme.header_background_rgb_hex -> "#3a3a3a"
|
|
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.
|
|
@@ -76,3 +76,54 @@ tag_name = verkit.tag_version(push=True)
|
|
|
76
76
|
# 4. Atomic release
|
|
77
77
|
verkit.release_version("patch", push=True)
|
|
78
78
|
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Shared Terminal Theme
|
|
83
|
+
|
|
84
|
+
`verkit` publishes the canonical Rich theme used across this developer's
|
|
85
|
+
CLIs (`task-agent`, `multi-agent-registry`, ...). It is **opt-in** — verkit
|
|
86
|
+
never applies the theme for you; consumers import it and apply it to their
|
|
87
|
+
own `Table`/`Panel` objects:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from rich.console import Console
|
|
91
|
+
from rich.table import Table
|
|
92
|
+
from verkit.theme import DEFAULT as theme
|
|
93
|
+
|
|
94
|
+
console = Console()
|
|
95
|
+
table = Table(
|
|
96
|
+
title="Portfolio",
|
|
97
|
+
box=theme.table_box, # borderless
|
|
98
|
+
header_style=theme.header_style, # "on grey23"
|
|
99
|
+
padding=theme.table_padding, # (0, 2, 0, 0)
|
|
100
|
+
)
|
|
101
|
+
console.print(table)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The theme is a frozen dataclass (`verkit.theme.Theme`); `DEFAULT` is the
|
|
105
|
+
canonical instance. Non-Python consumers (e.g. PowerShell scripts) read the
|
|
106
|
+
machine-readable mirror, which stays in sync via `tests/test_theme.py`:
|
|
107
|
+
|
|
108
|
+
```powershell
|
|
109
|
+
$theme = Invoke-RestMethod `
|
|
110
|
+
https://raw.githubusercontent.com/bizkite-co/verkit/main/src/verkit/theme.json
|
|
111
|
+
# $theme.theme.header_style -> "on grey23"
|
|
112
|
+
# $theme.theme.header_background_ansi -> ANSI 256 index 237 background escape
|
|
113
|
+
# $theme.theme.header_background_rgb_hex -> "#3a3a3a"
|
|
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.
|
|
@@ -10,16 +10,19 @@ from verkit.inspector import (
|
|
|
10
10
|
)
|
|
11
11
|
from verkit.promoter import promote_version
|
|
12
12
|
from verkit.tagger import release_version, tag_version
|
|
13
|
+
from verkit.theme import DEFAULT, Theme
|
|
13
14
|
|
|
14
|
-
__version__ = "0.
|
|
15
|
+
__version__ = "0.2.0"
|
|
15
16
|
__all__ = [
|
|
17
|
+
"DEFAULT",
|
|
18
|
+
"Theme",
|
|
16
19
|
"VersionInfo",
|
|
17
|
-
"
|
|
18
|
-
"inspect_committed",
|
|
20
|
+
"display_version_info",
|
|
19
21
|
"get_installed_version",
|
|
20
22
|
"get_latest_pypi_version",
|
|
21
|
-
"
|
|
23
|
+
"inspect_committed",
|
|
24
|
+
"inspect_project",
|
|
22
25
|
"promote_version",
|
|
23
|
-
"tag_version",
|
|
24
26
|
"release_version",
|
|
27
|
+
"tag_version",
|
|
25
28
|
]
|
|
@@ -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,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "verkit-grey",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"description": "Shared Rich terminal theme for tables and panels across this developer's Python CLIs (task-agent, multi-agent-registry, ...). Source of truth is src/verkit/theme.py (the Theme dataclass); this JSON mirrors Theme.DEFAULT for non-Python consumers and is kept in sync by tests/test_theme.py.",
|
|
5
|
+
"adoption": {
|
|
6
|
+
"python": "from verkit.theme import DEFAULT as theme",
|
|
7
|
+
"powershell": "$theme = Invoke-RestMethod '<raw URL of this file>'; $theme.theme",
|
|
8
|
+
"note": "Opt-in only: verkit publishes the theme but never applies it for you."
|
|
9
|
+
},
|
|
10
|
+
"theme": {
|
|
11
|
+
"table_box": null,
|
|
12
|
+
"panel_box": "MINIMAL",
|
|
13
|
+
"table_padding": [0, 2, 0, 0],
|
|
14
|
+
"header_style": "on grey23",
|
|
15
|
+
"header_background_ansi": "\u001b[48;5;237m",
|
|
16
|
+
"header_background_rgb_hex": "#3a3a3a"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Shared visual theme for Rich terminal output across client CLIs.
|
|
2
|
+
|
|
3
|
+
This module is a *convention carrier*, not a mandate. The theme is published
|
|
4
|
+
here so a client project can opt in with a one-line import instead of
|
|
5
|
+
maintaining its own copy; importing it is never forced on consumers.
|
|
6
|
+
|
|
7
|
+
Adoption hint (Python)::
|
|
8
|
+
|
|
9
|
+
from verkit.theme import DEFAULT as theme
|
|
10
|
+
|
|
11
|
+
table = Table(
|
|
12
|
+
title="...",
|
|
13
|
+
box=theme.table_box,
|
|
14
|
+
header_style=theme.header_style,
|
|
15
|
+
padding=theme.table_padding,
|
|
16
|
+
)
|
|
17
|
+
console.print(Panel(..., box=theme.panel_box))
|
|
18
|
+
|
|
19
|
+
Because ``Theme`` is a frozen dataclass, ``DEFAULT`` can be shared or
|
|
20
|
+
subclassed without risk of accidental mutation.
|
|
21
|
+
|
|
22
|
+
Non-Python consumers (e.g. PowerShell scripts) should read the machine
|
|
23
|
+
readable mirror at ``src/verkit/theme.json`` instead of re-deriving styles:
|
|
24
|
+
|
|
25
|
+
$theme = Invoke-RestMethod `
|
|
26
|
+
https://raw.githubusercontent.com/bizkite-co/verkit/main/src/verkit/theme.json
|
|
27
|
+
|
|
28
|
+
The JSON and this dataclass are kept in sync by ``tests/test_theme.py``.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
from dataclasses import dataclass
|
|
34
|
+
|
|
35
|
+
from rich import box as rich_box
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class Theme:
|
|
40
|
+
"""Visual theme for Rich terminal output (tables, panels).
|
|
41
|
+
|
|
42
|
+
Defaults render tables and panels in the compact report style shared
|
|
43
|
+
across this developer's CLIs: borderless tables, a minimal panel border,
|
|
44
|
+
and a subtle grey header band (ANSI 256 index 237).
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
table_box: rich_box.Box | None = None
|
|
48
|
+
panel_box: rich_box.Box = rich_box.MINIMAL
|
|
49
|
+
table_padding: tuple[int, int, int, int] = (0, 2, 0, 0)
|
|
50
|
+
header_style: str = "on grey23"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
#: The canonical theme. Client CLIs opt in with ``from verkit.theme import
|
|
54
|
+
#: DEFAULT``.
|
|
55
|
+
DEFAULT = Theme()
|
|
@@ -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"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from rich import box as rich_box
|
|
5
|
+
|
|
6
|
+
from verkit.theme import DEFAULT, Theme
|
|
7
|
+
|
|
8
|
+
THEME_JSON = Path(__file__).resolve().parent.parent / "src" / "verkit" / "theme.json"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_default_is_theme():
|
|
12
|
+
assert isinstance(DEFAULT, Theme)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_default_values():
|
|
16
|
+
assert DEFAULT.table_box is None
|
|
17
|
+
assert DEFAULT.panel_box is rich_box.MINIMAL
|
|
18
|
+
assert DEFAULT.table_padding == (0, 2, 0, 0)
|
|
19
|
+
assert DEFAULT.header_style == "on grey23"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_theme_json_matches_default():
|
|
23
|
+
data = json.loads(THEME_JSON.read_text(encoding="utf-8"))
|
|
24
|
+
theme = data["theme"]
|
|
25
|
+
assert theme["table_box"] is None
|
|
26
|
+
assert theme["panel_box"] == "MINIMAL"
|
|
27
|
+
assert tuple(theme["table_padding"]) == DEFAULT.table_padding
|
|
28
|
+
assert theme["header_style"] == DEFAULT.header_style
|
|
29
|
+
assert theme["header_background_ansi"] == "\x1b[48;5;237m"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_theme_is_frozen():
|
|
33
|
+
try:
|
|
34
|
+
DEFAULT.table_padding = (1, 1, 1, 1)
|
|
35
|
+
except AttributeError:
|
|
36
|
+
pass
|
|
37
|
+
else:
|
|
38
|
+
raise AssertionError("Theme should be immutable")
|
|
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
|