ramani 0.1.0a1__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.
Potentially problematic release.
This version of ramani might be problematic. Click here for more details.
- ramani-0.1.0a1/LICENSE +21 -0
- ramani-0.1.0a1/PKG-INFO +240 -0
- ramani-0.1.0a1/README.md +213 -0
- ramani-0.1.0a1/pyproject.toml +94 -0
- ramani-0.1.0a1/pyproject.toml.orig +70 -0
- ramani-0.1.0a1/src/ramani/__init__.py +7 -0
- ramani-0.1.0a1/src/ramani/__main__.py +6 -0
- ramani-0.1.0a1/src/ramani/cli.py +183 -0
- ramani-0.1.0a1/src/ramani/config.py +108 -0
- ramani-0.1.0a1/src/ramani/core.py +273 -0
- ramani-0.1.0a1/src/ramani/gitignore.py +117 -0
- ramani-0.1.0a1/src/ramani/py.typed +0 -0
- ramani-0.1.0a1/src/ramani/settings.py +58 -0
- ramani-0.1.0a1/src/ramani/tree.py +62 -0
ramani-0.1.0a1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kevin Wasike Wakhisi
|
|
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.
|
ramani-0.1.0a1/PKG-INFO
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ramani
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Summary: Ramani (Swahili for “map”): keeps each file's path as a comment on its first line, updates it when files move, and prints your project as a tree.
|
|
5
|
+
Keywords: comments,paths,tree,project-structure,cli,swahili
|
|
6
|
+
Author: Kevin Wasike Wakhisi
|
|
7
|
+
Author-email: Kevin Wasike Wakhisi <kevin@treeolive.tech>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Dist: pyperclip>=1.8
|
|
22
|
+
Maintainer: Kevin Wasike Wakhisi
|
|
23
|
+
Maintainer-email: Kevin Wasike Wakhisi <kevin@treeolive.tech>
|
|
24
|
+
Requires-Python: >=3.13
|
|
25
|
+
Project-URL: Repository, https://github.com/kvnwaks/ramani.git
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
<!-- ramani: README.md -->
|
|
29
|
+
<h1 align="center">ramani</h1>
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<strong>Every file knows where it lives.</strong><br>
|
|
33
|
+
A line at the top of each file says where it sits in your project, and it follows the file when it moves.
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<a href="https://www.python.org/"><img alt="Python 3.13+" src="https://img.shields.io/badge/python-3.13%2B-3776AB?logo=python&logoColor=white"></a>
|
|
38
|
+
<a href="https://docs.astral.sh/uv/"><img alt="Built with uv" src="https://img.shields.io/badge/built%20with-uv-DE5FE9"></a>
|
|
39
|
+
<img alt="MIT license" src="https://img.shields.io/badge/license-MIT-blue">
|
|
40
|
+
<img alt="ramani means map in Swahili" src="https://img.shields.io/badge/ramani-Swahili%20for%20%22map%22-006600">
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<p align="center">
|
|
44
|
+
<a href="#install">Install</a> ·
|
|
45
|
+
<a href="#usage">Usage</a> ·
|
|
46
|
+
<a href="#folder-tree">Folder tree</a> ·
|
|
47
|
+
<a href="#configuration">Configuration</a> ·
|
|
48
|
+
<a href="#supported-files">Supported files</a>
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
> **ramani** _(ra-MA-ni)_ is Swahili for **"map"**. That is the whole idea: ramani draws a map of your project _inside_ the project. Each file carries one comment with its own path, kept correct as files are renamed and moved, and the same tool can print the entire project as a tree.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# ramani: src/shop/billing/pay.py
|
|
55
|
+
def pay(): ...
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Why
|
|
59
|
+
|
|
60
|
+
Files travel without their address. They get pasted into chats, issues, code reviews and AI prompts, and the question "which file is this, again?" comes up every time. With ramani the path is part of the file, so it travels with it.
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
- 🧭 **Move-aware.** Rename or move a file, run ramani again, and its comment follows. Nothing is duplicated.
|
|
65
|
+
- 🪶 **Careful placement.** The comment goes after shebangs, encoding lines, XML declarations and Markdown front matter, and never turns a docstring into a plain string.
|
|
66
|
+
- 🗂️ **Knows 60+ file types.** Python, JavaScript, Go, Rust, SQL, HTML, CSS, Markdown, Dockerfile and more, each with the right comment syntax. Add your own in one line.
|
|
67
|
+
- 🙈 **Respects your ignores.** Built-in lists for `__pycache__`, `node_modules` and friends, every `.gitignore` it finds (nested ones too), and flags for the rest.
|
|
68
|
+
- 🌳 **Prints a folder tree.** One flag draws your project as a tree, another copies it to the clipboard.
|
|
69
|
+
- 🔍 **CI-friendly.** `--check` exits with code 1 when any comment is missing or stale. `--dry-run` writes nothing.
|
|
70
|
+
- 🧹 **Reversible.** `--remove` strips every comment ramani added.
|
|
71
|
+
|
|
72
|
+
## Install
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv tool install ramani # or: pipx install ramani / pip install ramani
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then, inside a project:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
ramani --dry-run # see what would change
|
|
82
|
+
ramani # do it
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
Point it at a folder (default: the current one) or a single file. Paths in the comments are relative to that folder.
|
|
88
|
+
|
|
89
|
+
```console
|
|
90
|
+
$ ramani
|
|
91
|
+
added docs/setup.md
|
|
92
|
+
added src/shop/cart.py
|
|
93
|
+
added src/shop/index.html
|
|
94
|
+
added src/shop/pay.py
|
|
95
|
+
added README.md
|
|
96
|
+
5 added, 1 unsupported
|
|
97
|
+
no comment style for: .json x1
|
|
98
|
+
add one with -c EXT=START[,END] or [tool.ramani.comments] in pyproject.toml
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The comment uses each language's own syntax, and sits where it can't break anything:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
# ramani: src/shop/cart.py
|
|
105
|
+
"""Cart logic."""
|
|
106
|
+
import json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<!-- ramani: src/shop/index.html -->
|
|
111
|
+
<h1>Hi</h1>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
---
|
|
116
|
+
title: Setup
|
|
117
|
+
---
|
|
118
|
+
<!-- ramani: docs/setup.md -->
|
|
119
|
+
# Setup
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### When a file moves
|
|
123
|
+
|
|
124
|
+
Move `pay.py` into a new `billing/` folder and run ramani again. It recognises its own comment, sees the path is out of date, and rewrites it. Untouched files stay untouched.
|
|
125
|
+
|
|
126
|
+
```console
|
|
127
|
+
$ mkdir src/shop/billing && mv src/shop/pay.py src/shop/billing/
|
|
128
|
+
$ ramani
|
|
129
|
+
updated src/shop/billing/pay.py
|
|
130
|
+
4 unchanged, 1 unsupported, 1 updated
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
# ramani: src/shop/billing/pay.py
|
|
135
|
+
def pay(): ...
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Add `ramani --check` to CI or a pre-commit hook to catch stale comments before they land. It changes nothing and exits with 1 if anything needs updating.
|
|
139
|
+
|
|
140
|
+
## Folder tree
|
|
141
|
+
|
|
142
|
+
`--tree` prints your project as a tree and writes nothing. It uses the same ignore rules as marking, so you see exactly the files ramani works on. Folders come first.
|
|
143
|
+
|
|
144
|
+
```console
|
|
145
|
+
$ ramani --tree
|
|
146
|
+
shop/
|
|
147
|
+
├── docs/
|
|
148
|
+
│ └── setup.md
|
|
149
|
+
├── src/
|
|
150
|
+
│ └── shop/
|
|
151
|
+
│ ├── billing/
|
|
152
|
+
│ │ └── pay.py
|
|
153
|
+
│ ├── cart.py
|
|
154
|
+
│ └── index.html
|
|
155
|
+
├── README.md
|
|
156
|
+
└── data.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Add `--copy` to also put the tree on your clipboard, ready to paste into a chat, an issue or a doc.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
ramani --copy # print the tree and copy it
|
|
163
|
+
ramani ./src -t -d tests # tree of ./src, leaving out tests
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
> On Linux, the clipboard needs `xclip`, `xsel` or `wl-clipboard` installed. If none is available the tree is still printed, with a note on stderr and exit code 1.
|
|
167
|
+
|
|
168
|
+
## Options
|
|
169
|
+
|
|
170
|
+
| Option | What it does |
|
|
171
|
+
| ------------------------------- | ------------------------------------------------------------------- |
|
|
172
|
+
| `PATH` | Folder or file to process (default: current folder) |
|
|
173
|
+
| `-e, --ignore-ext EXT` | Also skip this extension, e.g. `-e .txt` (repeatable) |
|
|
174
|
+
| `-d, --ignore-dir NAME` | Also skip this folder name or glob, e.g. `-d "build*"` (repeatable) |
|
|
175
|
+
| `-c, --comment EXT=START[,END]` | Comment style for an extension or file name (repeatable) |
|
|
176
|
+
| `--no-gitignore` | Ignore `.gitignore` files |
|
|
177
|
+
| `-n, --dry-run` | Show what would change, write nothing |
|
|
178
|
+
| `--check` | Dry run that exits 1 if anything is missing or stale |
|
|
179
|
+
| `--remove` | Strip ramani comments instead of adding them |
|
|
180
|
+
| `-t, --tree` | Print the folder tree instead of marking files |
|
|
181
|
+
| `--copy` | Copy the tree to the clipboard (implies `--tree`) |
|
|
182
|
+
| `-V, --version` | Print the installed version and exit |
|
|
183
|
+
| `-v, --verbose` | Also list unchanged files |
|
|
184
|
+
|
|
185
|
+
## Configuration
|
|
186
|
+
|
|
187
|
+
Teach ramani a file type on the command line:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
ramani -c .abc=// # line comment
|
|
191
|
+
ramani -c ".tpl=<!--,-->" # block comment: start,end
|
|
192
|
+
ramani -c Jenkinsfile=// # by exact file name
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
or once and for all in your `pyproject.toml`:
|
|
196
|
+
|
|
197
|
+
```toml
|
|
198
|
+
[tool.ramani.comments]
|
|
199
|
+
".abc" = "//"
|
|
200
|
+
".tpl" = ["<!--", "-->"]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Flags beat `pyproject.toml`, which beats the built-in styles, so you can override those too.
|
|
204
|
+
|
|
205
|
+
## Supported files
|
|
206
|
+
|
|
207
|
+
| Comment | Looks like | Files |
|
|
208
|
+
| ---------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
209
|
+
| `#` | `# ramani: a/b.py` | `.py` `.pyi` `.sh` `.rb` `.pl` `.r` `.jl` `.ex` `.yml` `.yaml` `.toml` `.cfg` `.tf` `.ps1` `.nix` `Dockerfile` `Makefile` `.gitignore` … |
|
|
210
|
+
| `//` | `// ramani: a/b.ts` | `.js` `.mjs` `.jsx` `.ts` `.tsx` `.java` `.kt` `.scala` `.c` `.h` `.cpp` `.cs` `.go` `.rs` `.swift` `.dart` `.scss` `.proto` … |
|
|
211
|
+
| `--` | `-- ramani: a/b.sql` | `.sql` `.lua` `.hs` `.elm` |
|
|
212
|
+
| `<!-- -->` | `<!-- ramani: a/b.html -->` | `.html` `.htm` `.xml` `.svg` `.vue` `.svelte` `.md` `.markdown` |
|
|
213
|
+
| `/* */` | `/* ramani: a/b.css */` | `.css` |
|
|
214
|
+
| `{# #}` | `{# ramani: a/b.j2 #}` | `.j2` `.jinja` `.jinja2` |
|
|
215
|
+
| `{/* */}` | `{/* ramani: a/b.mdx */}` | `.mdx` |
|
|
216
|
+
| `%` | `% ramani: a/b.tex` | `.tex` |
|
|
217
|
+
|
|
218
|
+
Formats with no comment syntax, such as `.json` and `.csv`, are left alone and listed at the end of each run so nothing is skipped silently. The full table lives in `src/ramani/config.py`.
|
|
219
|
+
|
|
220
|
+
## How it works
|
|
221
|
+
|
|
222
|
+
1. Skip anything that must stay first: a shebang, an encoding line, an XML declaration, a `# syntax=` line, or front matter in Markdown.
|
|
223
|
+
2. Look at the next line. If it is a ramani comment with the **same** path, do nothing.
|
|
224
|
+
3. If it is a ramani comment with a **different** path, the file has moved. Rewrite that one line.
|
|
225
|
+
4. If it is not a ramani comment, insert one.
|
|
226
|
+
|
|
227
|
+
Line endings (LF or CRLF) and UTF-8 byte-order marks are preserved. Symlinks, files that are not UTF-8, and binary files (anything containing a NUL byte) are never touched.
|
|
228
|
+
|
|
229
|
+
## Development
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
uv sync
|
|
233
|
+
uv run poe test
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Ramani has a single runtime dependency, [`pyperclip`](https://pypi.org/project/pyperclip/), used only for `--copy`.
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
MIT © Kevin Wasike Wakhisi. Use it, change it, ship it; just keep the copyright notice.
|
ramani-0.1.0a1/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
<!-- ramani: README.md -->
|
|
2
|
+
<h1 align="center">ramani</h1>
|
|
3
|
+
|
|
4
|
+
<p align="center">
|
|
5
|
+
<strong>Every file knows where it lives.</strong><br>
|
|
6
|
+
A line at the top of each file says where it sits in your project, and it follows the file when it moves.
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://www.python.org/"><img alt="Python 3.13+" src="https://img.shields.io/badge/python-3.13%2B-3776AB?logo=python&logoColor=white"></a>
|
|
11
|
+
<a href="https://docs.astral.sh/uv/"><img alt="Built with uv" src="https://img.shields.io/badge/built%20with-uv-DE5FE9"></a>
|
|
12
|
+
<img alt="MIT license" src="https://img.shields.io/badge/license-MIT-blue">
|
|
13
|
+
<img alt="ramani means map in Swahili" src="https://img.shields.io/badge/ramani-Swahili%20for%20%22map%22-006600">
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="#install">Install</a> ·
|
|
18
|
+
<a href="#usage">Usage</a> ·
|
|
19
|
+
<a href="#folder-tree">Folder tree</a> ·
|
|
20
|
+
<a href="#configuration">Configuration</a> ·
|
|
21
|
+
<a href="#supported-files">Supported files</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
> **ramani** _(ra-MA-ni)_ is Swahili for **"map"**. That is the whole idea: ramani draws a map of your project _inside_ the project. Each file carries one comment with its own path, kept correct as files are renamed and moved, and the same tool can print the entire project as a tree.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
# ramani: src/shop/billing/pay.py
|
|
28
|
+
def pay(): ...
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Why
|
|
32
|
+
|
|
33
|
+
Files travel without their address. They get pasted into chats, issues, code reviews and AI prompts, and the question "which file is this, again?" comes up every time. With ramani the path is part of the file, so it travels with it.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- 🧭 **Move-aware.** Rename or move a file, run ramani again, and its comment follows. Nothing is duplicated.
|
|
38
|
+
- 🪶 **Careful placement.** The comment goes after shebangs, encoding lines, XML declarations and Markdown front matter, and never turns a docstring into a plain string.
|
|
39
|
+
- 🗂️ **Knows 60+ file types.** Python, JavaScript, Go, Rust, SQL, HTML, CSS, Markdown, Dockerfile and more, each with the right comment syntax. Add your own in one line.
|
|
40
|
+
- 🙈 **Respects your ignores.** Built-in lists for `__pycache__`, `node_modules` and friends, every `.gitignore` it finds (nested ones too), and flags for the rest.
|
|
41
|
+
- 🌳 **Prints a folder tree.** One flag draws your project as a tree, another copies it to the clipboard.
|
|
42
|
+
- 🔍 **CI-friendly.** `--check` exits with code 1 when any comment is missing or stale. `--dry-run` writes nothing.
|
|
43
|
+
- 🧹 **Reversible.** `--remove` strips every comment ramani added.
|
|
44
|
+
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv tool install ramani # or: pipx install ramani / pip install ramani
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Then, inside a project:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
ramani --dry-run # see what would change
|
|
55
|
+
ramani # do it
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
Point it at a folder (default: the current one) or a single file. Paths in the comments are relative to that folder.
|
|
61
|
+
|
|
62
|
+
```console
|
|
63
|
+
$ ramani
|
|
64
|
+
added docs/setup.md
|
|
65
|
+
added src/shop/cart.py
|
|
66
|
+
added src/shop/index.html
|
|
67
|
+
added src/shop/pay.py
|
|
68
|
+
added README.md
|
|
69
|
+
5 added, 1 unsupported
|
|
70
|
+
no comment style for: .json x1
|
|
71
|
+
add one with -c EXT=START[,END] or [tool.ramani.comments] in pyproject.toml
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The comment uses each language's own syntax, and sits where it can't break anything:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# ramani: src/shop/cart.py
|
|
78
|
+
"""Cart logic."""
|
|
79
|
+
import json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```html
|
|
83
|
+
<!-- ramani: src/shop/index.html -->
|
|
84
|
+
<h1>Hi</h1>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
---
|
|
89
|
+
title: Setup
|
|
90
|
+
---
|
|
91
|
+
<!-- ramani: docs/setup.md -->
|
|
92
|
+
# Setup
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### When a file moves
|
|
96
|
+
|
|
97
|
+
Move `pay.py` into a new `billing/` folder and run ramani again. It recognises its own comment, sees the path is out of date, and rewrites it. Untouched files stay untouched.
|
|
98
|
+
|
|
99
|
+
```console
|
|
100
|
+
$ mkdir src/shop/billing && mv src/shop/pay.py src/shop/billing/
|
|
101
|
+
$ ramani
|
|
102
|
+
updated src/shop/billing/pay.py
|
|
103
|
+
4 unchanged, 1 unsupported, 1 updated
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
# ramani: src/shop/billing/pay.py
|
|
108
|
+
def pay(): ...
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Add `ramani --check` to CI or a pre-commit hook to catch stale comments before they land. It changes nothing and exits with 1 if anything needs updating.
|
|
112
|
+
|
|
113
|
+
## Folder tree
|
|
114
|
+
|
|
115
|
+
`--tree` prints your project as a tree and writes nothing. It uses the same ignore rules as marking, so you see exactly the files ramani works on. Folders come first.
|
|
116
|
+
|
|
117
|
+
```console
|
|
118
|
+
$ ramani --tree
|
|
119
|
+
shop/
|
|
120
|
+
├── docs/
|
|
121
|
+
│ └── setup.md
|
|
122
|
+
├── src/
|
|
123
|
+
│ └── shop/
|
|
124
|
+
│ ├── billing/
|
|
125
|
+
│ │ └── pay.py
|
|
126
|
+
│ ├── cart.py
|
|
127
|
+
│ └── index.html
|
|
128
|
+
├── README.md
|
|
129
|
+
└── data.json
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Add `--copy` to also put the tree on your clipboard, ready to paste into a chat, an issue or a doc.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
ramani --copy # print the tree and copy it
|
|
136
|
+
ramani ./src -t -d tests # tree of ./src, leaving out tests
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
> On Linux, the clipboard needs `xclip`, `xsel` or `wl-clipboard` installed. If none is available the tree is still printed, with a note on stderr and exit code 1.
|
|
140
|
+
|
|
141
|
+
## Options
|
|
142
|
+
|
|
143
|
+
| Option | What it does |
|
|
144
|
+
| ------------------------------- | ------------------------------------------------------------------- |
|
|
145
|
+
| `PATH` | Folder or file to process (default: current folder) |
|
|
146
|
+
| `-e, --ignore-ext EXT` | Also skip this extension, e.g. `-e .txt` (repeatable) |
|
|
147
|
+
| `-d, --ignore-dir NAME` | Also skip this folder name or glob, e.g. `-d "build*"` (repeatable) |
|
|
148
|
+
| `-c, --comment EXT=START[,END]` | Comment style for an extension or file name (repeatable) |
|
|
149
|
+
| `--no-gitignore` | Ignore `.gitignore` files |
|
|
150
|
+
| `-n, --dry-run` | Show what would change, write nothing |
|
|
151
|
+
| `--check` | Dry run that exits 1 if anything is missing or stale |
|
|
152
|
+
| `--remove` | Strip ramani comments instead of adding them |
|
|
153
|
+
| `-t, --tree` | Print the folder tree instead of marking files |
|
|
154
|
+
| `--copy` | Copy the tree to the clipboard (implies `--tree`) |
|
|
155
|
+
| `-V, --version` | Print the installed version and exit |
|
|
156
|
+
| `-v, --verbose` | Also list unchanged files |
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
Teach ramani a file type on the command line:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
ramani -c .abc=// # line comment
|
|
164
|
+
ramani -c ".tpl=<!--,-->" # block comment: start,end
|
|
165
|
+
ramani -c Jenkinsfile=// # by exact file name
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
or once and for all in your `pyproject.toml`:
|
|
169
|
+
|
|
170
|
+
```toml
|
|
171
|
+
[tool.ramani.comments]
|
|
172
|
+
".abc" = "//"
|
|
173
|
+
".tpl" = ["<!--", "-->"]
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Flags beat `pyproject.toml`, which beats the built-in styles, so you can override those too.
|
|
177
|
+
|
|
178
|
+
## Supported files
|
|
179
|
+
|
|
180
|
+
| Comment | Looks like | Files |
|
|
181
|
+
| ---------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
182
|
+
| `#` | `# ramani: a/b.py` | `.py` `.pyi` `.sh` `.rb` `.pl` `.r` `.jl` `.ex` `.yml` `.yaml` `.toml` `.cfg` `.tf` `.ps1` `.nix` `Dockerfile` `Makefile` `.gitignore` … |
|
|
183
|
+
| `//` | `// ramani: a/b.ts` | `.js` `.mjs` `.jsx` `.ts` `.tsx` `.java` `.kt` `.scala` `.c` `.h` `.cpp` `.cs` `.go` `.rs` `.swift` `.dart` `.scss` `.proto` … |
|
|
184
|
+
| `--` | `-- ramani: a/b.sql` | `.sql` `.lua` `.hs` `.elm` |
|
|
185
|
+
| `<!-- -->` | `<!-- ramani: a/b.html -->` | `.html` `.htm` `.xml` `.svg` `.vue` `.svelte` `.md` `.markdown` |
|
|
186
|
+
| `/* */` | `/* ramani: a/b.css */` | `.css` |
|
|
187
|
+
| `{# #}` | `{# ramani: a/b.j2 #}` | `.j2` `.jinja` `.jinja2` |
|
|
188
|
+
| `{/* */}` | `{/* ramani: a/b.mdx */}` | `.mdx` |
|
|
189
|
+
| `%` | `% ramani: a/b.tex` | `.tex` |
|
|
190
|
+
|
|
191
|
+
Formats with no comment syntax, such as `.json` and `.csv`, are left alone and listed at the end of each run so nothing is skipped silently. The full table lives in `src/ramani/config.py`.
|
|
192
|
+
|
|
193
|
+
## How it works
|
|
194
|
+
|
|
195
|
+
1. Skip anything that must stay first: a shebang, an encoding line, an XML declaration, a `# syntax=` line, or front matter in Markdown.
|
|
196
|
+
2. Look at the next line. If it is a ramani comment with the **same** path, do nothing.
|
|
197
|
+
3. If it is a ramani comment with a **different** path, the file has moved. Rewrite that one line.
|
|
198
|
+
4. If it is not a ramani comment, insert one.
|
|
199
|
+
|
|
200
|
+
Line endings (LF or CRLF) and UTF-8 byte-order marks are preserved. Symlinks, files that are not UTF-8, and binary files (anything containing a NUL byte) are never touched.
|
|
201
|
+
|
|
202
|
+
## Development
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
uv sync
|
|
206
|
+
uv run poe test
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Ramani has a single runtime dependency, [`pyperclip`](https://pypi.org/project/pyperclip/), used only for `--copy`.
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT © Kevin Wasike Wakhisi. Use it, change it, ship it; just keep the copyright notice.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.13.7,<0.14.0"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ramani"
|
|
7
|
+
version = "0.1.0a1"
|
|
8
|
+
description = "Ramani (Swahili for “map”): keeps each file's path as a comment on its first line, updates it when files move, and prints your project as a tree."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.13"
|
|
13
|
+
keywords = [
|
|
14
|
+
"comments",
|
|
15
|
+
"paths",
|
|
16
|
+
"tree",
|
|
17
|
+
"project-structure",
|
|
18
|
+
"cli",
|
|
19
|
+
"swahili",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 3 - Alpha",
|
|
23
|
+
"Environment :: Console",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"Operating System :: OS Independent",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Programming Language :: Python :: 3.14",
|
|
30
|
+
"Programming Language :: Python :: 3.15",
|
|
31
|
+
"Topic :: Software Development",
|
|
32
|
+
"Topic :: Utilities",
|
|
33
|
+
]
|
|
34
|
+
dependencies = ["pyperclip>=1.8"]
|
|
35
|
+
|
|
36
|
+
[[project.authors]]
|
|
37
|
+
name = "Kevin Wasike Wakhisi"
|
|
38
|
+
email = "kevin@treeolive.tech"
|
|
39
|
+
|
|
40
|
+
[[project.maintainers]]
|
|
41
|
+
name = "Kevin Wasike Wakhisi"
|
|
42
|
+
email = "kevin@treeolive.tech"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
ramani = "ramani:main"
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Repository = "https://github.com/kvnwaks/ramani.git"
|
|
49
|
+
|
|
50
|
+
[dependency-groups]
|
|
51
|
+
dev = [
|
|
52
|
+
"pytest>=9.2.0",
|
|
53
|
+
"poethepoet>=0.49.0",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.uv]
|
|
57
|
+
preview-features = [
|
|
58
|
+
"format-command",
|
|
59
|
+
"check-command",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[[tool.uv.index]]
|
|
63
|
+
name = "testpypi"
|
|
64
|
+
url = "https://test.pypi.org/simple/"
|
|
65
|
+
publish-url = "https://test.pypi.org/legacy/"
|
|
66
|
+
explicit = true
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
pythonpath = ["src"]
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
|
|
72
|
+
[[tool.poe.tasks.format.sequence]]
|
|
73
|
+
cmd = "uv format"
|
|
74
|
+
|
|
75
|
+
[[tool.poe.tasks.lint.sequence]]
|
|
76
|
+
cmd = "uv lock --check"
|
|
77
|
+
|
|
78
|
+
[[tool.poe.tasks.lint.sequence]]
|
|
79
|
+
cmd = "uv format --check"
|
|
80
|
+
|
|
81
|
+
[[tool.poe.tasks.lint.sequence]]
|
|
82
|
+
cmd = "uv check"
|
|
83
|
+
|
|
84
|
+
[[tool.poe.tasks.test.sequence]]
|
|
85
|
+
cmd = "uv run pytest"
|
|
86
|
+
|
|
87
|
+
[[tool.poe.tasks.add.sequence]]
|
|
88
|
+
ref = "lint"
|
|
89
|
+
|
|
90
|
+
[[tool.poe.tasks.add.sequence]]
|
|
91
|
+
ref = "test"
|
|
92
|
+
|
|
93
|
+
[[tool.poe.tasks.add.sequence]]
|
|
94
|
+
cmd = "git add ."
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# ramani: pyproject.toml
|
|
2
|
+
[build-system]
|
|
3
|
+
requires = ["uv_build>=0.13.7,<0.14.0"]
|
|
4
|
+
build-backend = "uv_build"
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "ramani"
|
|
8
|
+
version = "0.1.0a1"
|
|
9
|
+
description = "Ramani (Swahili for “map”): keeps each file's path as a comment on its first line, updates it when files move, and prints your project as a tree."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Kevin Wasike Wakhisi", email = "kevin@treeolive.tech" }]
|
|
14
|
+
maintainers = [
|
|
15
|
+
{ name = "Kevin Wasike Wakhisi", email = "kevin@treeolive.tech" },
|
|
16
|
+
]
|
|
17
|
+
requires-python = ">=3.13"
|
|
18
|
+
keywords = ["comments", "paths", "tree", "project-structure", "cli", "swahili"]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Environment :: Console",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Programming Language :: Python :: 3.15",
|
|
29
|
+
"Topic :: Software Development",
|
|
30
|
+
"Topic :: Utilities",
|
|
31
|
+
]
|
|
32
|
+
dependencies = ["pyperclip>=1.8"]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
ramani = "ramani:main"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Repository = "https://github.com/kvnwaks/ramani.git"
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
dev = ["pytest>=9.2.0", "poethepoet>=0.49.0"]
|
|
42
|
+
|
|
43
|
+
[tool.uv]
|
|
44
|
+
preview-features = ["format-command", "check-command"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
pythonpath = ["src"]
|
|
48
|
+
testpaths = ["tests"]
|
|
49
|
+
|
|
50
|
+
[tool.poe.tasks.format]
|
|
51
|
+
sequence = [{ cmd = "uv format" }]
|
|
52
|
+
|
|
53
|
+
[tool.poe.tasks.lint]
|
|
54
|
+
sequence = [
|
|
55
|
+
{ cmd = "uv lock --check" },
|
|
56
|
+
{ cmd = "uv format --check" },
|
|
57
|
+
{ cmd = "uv check" },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.poe.tasks.test]
|
|
61
|
+
sequence = [{ cmd = "uv run pytest" }]
|
|
62
|
+
|
|
63
|
+
[tool.poe.tasks.add]
|
|
64
|
+
sequence = [{ ref = "lint" }, { ref = "test" }, { cmd = "git add ." }]
|
|
65
|
+
|
|
66
|
+
[[tool.uv.index]]
|
|
67
|
+
name = "testpypi"
|
|
68
|
+
url = "https://test.pypi.org/simple/"
|
|
69
|
+
publish-url = "https://test.pypi.org/legacy/"
|
|
70
|
+
explicit = true
|