tidyout-cli 0.1.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.
@@ -0,0 +1,42 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.11"
17
+
18
+ - name: Install dependencies
19
+ run: pip install -e ".[dev]"
20
+
21
+ - name: Run tests
22
+ run: pytest
23
+
24
+ publish:
25
+ needs: test
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+
30
+ - uses: actions/setup-python@v5
31
+ with:
32
+ python-version: "3.11"
33
+
34
+ - name: Build package
35
+ run: |
36
+ pip install build
37
+ python -m build
38
+
39
+ - name: Publish to PyPI
40
+ uses: pypa/gh-action-pypi-publish@release/v1
41
+ with:
42
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,17 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .eggs/
6
+ *.egg-info/
7
+ dist/
8
+ build/
9
+ .venv/
10
+ venv/
11
+ env/
12
+ .env
13
+ .pytest_cache/
14
+ .coverage
15
+ htmlcov/
16
+ *.egg
17
+ .DS_Store
@@ -0,0 +1,78 @@
1
+ # Contributing a New Rule
2
+
3
+ Adding support for a new command is just one file.
4
+
5
+ ## Steps
6
+
7
+ 1. Create `tidyout/rules/<command>.yaml`
8
+ 2. Follow the schema below
9
+ 3. Add a test in `tests/test_stripper.py`
10
+ 4. Open a PR
11
+
12
+ ## YAML Schema
13
+
14
+ ```yaml
15
+ strip:
16
+ - pattern: "string or regex"
17
+ type: prefix | contains | exact | regex
18
+
19
+ keep:
20
+ - pattern: "string or regex"
21
+ type: prefix | contains | exact | regex
22
+ ```
23
+
24
+ **`type` options:**
25
+
26
+ | Type | Behaviour |
27
+ |---|---|
28
+ | `prefix` | `line.startswith(pattern)` |
29
+ | `contains` | `pattern in line` |
30
+ | `exact` | `line == pattern` |
31
+ | `regex` | `re.search(pattern, line)` |
32
+
33
+ **Evaluation order:** `keep` always wins over `strip`. If a line matches a keep pattern it is always kept, even if it also matches a strip pattern.
34
+
35
+ ## Two-word commands
36
+
37
+ For commands like `git status` or `docker logs`, name the file using an underscore:
38
+ `git_status.yaml`, `docker_logs.yaml`, `npm_install.yaml`
39
+
40
+ tidyout automatically tries the two-word key first, then falls back to the single-word key.
41
+
42
+ ## Transform hooks (optional)
43
+
44
+ If the rule needs to reformat lines rather than just filter them (e.g., shorten a hash, reorder output), add a Python function in `tidyout/stripper.py` and register it in the `TRANSFORMS` dict:
45
+
46
+ ```python
47
+ def transform_mycommand(lines):
48
+ # lines: filtered output after YAML rules applied
49
+ # return: final list of lines
50
+ return [line.upper() for line in lines]
51
+
52
+ TRANSFORMS = {
53
+ ...
54
+ "mycommand": transform_mycommand,
55
+ }
56
+ ```
57
+
58
+ ## Example rule
59
+
60
+ `tidyout/rules/rspec.yaml` — strip passing examples, keep failures and summary:
61
+
62
+ ```yaml
63
+ strip:
64
+ - pattern: "^ \\."
65
+ type: regex
66
+ - pattern: "^$"
67
+ type: exact
68
+
69
+ keep:
70
+ - pattern: "example"
71
+ type: contains
72
+ - pattern: "failure"
73
+ type: contains
74
+ - pattern: "Failure/Error"
75
+ type: contains
76
+ - pattern: "rspec"
77
+ type: contains
78
+ ```
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: tidyout-cli
3
+ Version: 0.1.0
4
+ Summary: CLI wrapper that compresses command output for AI agents, reducing token usage by 60-97%
5
+ License: MIT
6
+ Keywords: ai,cli,compression,developer-tools,tokens
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: Utilities
18
+ Requires-Python: >=3.9
19
+ Requires-Dist: pyyaml>=6.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=7.0; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # tidyout
25
+
26
+ A CLI wrapper that sits between you (or your AI agent) and the terminal. Prefix any command with `tidy` and tidyout executes it, strips the noise, and returns a compressed version with a token savings summary.
27
+
28
+ ```
29
+ ✂️ tidyout: 312 lines → 18 lines | saved ~2376 tokens (94% reduction)
30
+ ```
31
+
32
+ Built for AI coding sessions (Claude Code, Cursor, Copilot) where terminal output burns tokens fast.
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ pip install tidyout-cli
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```bash
43
+ tidy pytest tests/
44
+ tidy git status
45
+ tidy git log
46
+ tidy ls -la
47
+ tidy find . -name "*.py"
48
+ tidy docker logs mycontainer
49
+ tidy npm install
50
+ tidy cargo build
51
+ ```
52
+
53
+ Every run ends with a summary line:
54
+
55
+ ```
56
+ ✂️ tidyout: {original} lines → {compressed} lines | saved ~{tokens} tokens ({percent}% reduction)
57
+ ```
58
+
59
+ Commands without a matching rule pass through unchanged and still show the summary (0% reduction).
60
+
61
+ ## Supported commands
62
+
63
+ | Command | What gets stripped | Typical savings |
64
+ |---|---|---|
65
+ | `pytest` | PASSED lines, platform info, separators | ~80% |
66
+ | `git status` | hints, up-to-date messages, blank lines | ~60% |
67
+ | `git log` | Author email, full hashes, blank lines | ~50% |
68
+ | `ls` | permissions, owner, group, timestamp — reformatted to `DIR:name` / `F:name:size` | ~70% |
69
+ | `find` | `.git/`, `node_modules/`, `__pycache__/`, `./` prefix | ~40% |
70
+ | `tree` | entries beyond depth 3, `node_modules`, `.git`, `__pycache__` | ~50% |
71
+ | `pip install` | progress bars, Collecting/Downloading, already-satisfied | ~90% |
72
+ | `npm install` | warn/notice lines, blank lines | ~85% |
73
+ | `docker logs` | health check noise, keeps last 50 lines | ~70% |
74
+ | `docker ps` | header, full IDs, padding — reformatted to `name:status:ports` | ~60% |
75
+ | `jest` | passing test names, keeps failures and summary | ~80% |
76
+ | `make` | Entering/Leaving directory noise | ~40% |
77
+ | `cargo build` | dependency Compiling lines, Downloading | ~75% |
78
+
79
+ ## `tidy init` — inject rules into your AI agent config
80
+
81
+ Running `tidy init` in your project directory automatically tells your AI agent to always use `tidy`:
82
+
83
+ ```bash
84
+ tidy init
85
+ ```
86
+
87
+ It detects which config file to update:
88
+
89
+ - `CLAUDE.md` exists → appends tidyout usage rules
90
+ - `.cursorrules` exists → appends tidyout usage rules
91
+ - Neither exists → creates `CLAUDE.md` with the rules
92
+
93
+ Injected block:
94
+
95
+ ```markdown
96
+ ## Token Optimization with tidyout
97
+ Always prefix the following commands with `tidy` when running in terminal:
98
+ pytest, npm test, jest, rspec, git status, git log, ls, find, tree,
99
+ pip install, npm install, docker logs, docker ps, make, cargo build
100
+ This reduces token usage by 60-97% on command outputs.
101
+ ```
102
+
103
+ ## Contributing a new rule
104
+
105
+ See [CONTRIBUTING.md](CONTRIBUTING.md) — it's just one YAML file.
@@ -0,0 +1,82 @@
1
+ # tidyout
2
+
3
+ A CLI wrapper that sits between you (or your AI agent) and the terminal. Prefix any command with `tidy` and tidyout executes it, strips the noise, and returns a compressed version with a token savings summary.
4
+
5
+ ```
6
+ ✂️ tidyout: 312 lines → 18 lines | saved ~2376 tokens (94% reduction)
7
+ ```
8
+
9
+ Built for AI coding sessions (Claude Code, Cursor, Copilot) where terminal output burns tokens fast.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install tidyout-cli
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```bash
20
+ tidy pytest tests/
21
+ tidy git status
22
+ tidy git log
23
+ tidy ls -la
24
+ tidy find . -name "*.py"
25
+ tidy docker logs mycontainer
26
+ tidy npm install
27
+ tidy cargo build
28
+ ```
29
+
30
+ Every run ends with a summary line:
31
+
32
+ ```
33
+ ✂️ tidyout: {original} lines → {compressed} lines | saved ~{tokens} tokens ({percent}% reduction)
34
+ ```
35
+
36
+ Commands without a matching rule pass through unchanged and still show the summary (0% reduction).
37
+
38
+ ## Supported commands
39
+
40
+ | Command | What gets stripped | Typical savings |
41
+ |---|---|---|
42
+ | `pytest` | PASSED lines, platform info, separators | ~80% |
43
+ | `git status` | hints, up-to-date messages, blank lines | ~60% |
44
+ | `git log` | Author email, full hashes, blank lines | ~50% |
45
+ | `ls` | permissions, owner, group, timestamp — reformatted to `DIR:name` / `F:name:size` | ~70% |
46
+ | `find` | `.git/`, `node_modules/`, `__pycache__/`, `./` prefix | ~40% |
47
+ | `tree` | entries beyond depth 3, `node_modules`, `.git`, `__pycache__` | ~50% |
48
+ | `pip install` | progress bars, Collecting/Downloading, already-satisfied | ~90% |
49
+ | `npm install` | warn/notice lines, blank lines | ~85% |
50
+ | `docker logs` | health check noise, keeps last 50 lines | ~70% |
51
+ | `docker ps` | header, full IDs, padding — reformatted to `name:status:ports` | ~60% |
52
+ | `jest` | passing test names, keeps failures and summary | ~80% |
53
+ | `make` | Entering/Leaving directory noise | ~40% |
54
+ | `cargo build` | dependency Compiling lines, Downloading | ~75% |
55
+
56
+ ## `tidy init` — inject rules into your AI agent config
57
+
58
+ Running `tidy init` in your project directory automatically tells your AI agent to always use `tidy`:
59
+
60
+ ```bash
61
+ tidy init
62
+ ```
63
+
64
+ It detects which config file to update:
65
+
66
+ - `CLAUDE.md` exists → appends tidyout usage rules
67
+ - `.cursorrules` exists → appends tidyout usage rules
68
+ - Neither exists → creates `CLAUDE.md` with the rules
69
+
70
+ Injected block:
71
+
72
+ ```markdown
73
+ ## Token Optimization with tidyout
74
+ Always prefix the following commands with `tidy` when running in terminal:
75
+ pytest, npm test, jest, rspec, git status, git log, ls, find, tree,
76
+ pip install, npm install, docker logs, docker ps, make, cargo build
77
+ This reduces token usage by 60-97% on command outputs.
78
+ ```
79
+
80
+ ## Contributing a new rule
81
+
82
+ See [CONTRIBUTING.md](CONTRIBUTING.md) — it's just one YAML file.
@@ -0,0 +1,169 @@
1
+ # tidyout — Design Spec
2
+ **Date:** 2026-05-12
3
+ **Status:** Approved
4
+
5
+ ---
6
+
7
+ ## What It Does
8
+
9
+ `tidyout` is a Python CLI wrapper that sits between an AI agent (Claude Code, Cursor, etc.) and the terminal. When a command is prefixed with `tidy`, tidyout:
10
+
11
+ 1. Executes the real command and captures stdout + stderr
12
+ 2. Looks up a strip/keep rule for the command
13
+ 3. Applies the rule to compress the output
14
+ 4. Prints the compressed output followed by a token savings summary
15
+
16
+ Goal: reduce token consumption in AI agent sessions by stripping human-pretty noise and returning machine-readable output.
17
+
18
+ ---
19
+
20
+ ## CLI Usage
21
+
22
+ ```bash
23
+ tidy pytest tests/
24
+ tidy git status
25
+ tidy ls -la
26
+ tidy docker logs mycontainer
27
+ tidy init
28
+ ```
29
+
30
+ Every run ends with:
31
+ ```
32
+ ✂️ tidyout: {original} lines → {compressed} lines | saved ~{tokens} tokens ({percent}% reduction)
33
+ ```
34
+
35
+ Token estimate: `1 token ≈ 4 characters`. Savings calculated from character count difference.
36
+
37
+ ---
38
+
39
+ ## Architecture
40
+
41
+ ```
42
+ tidy <cmd> [args]
43
+
44
+
45
+ cli.py:main()
46
+
47
+ ├── cmd == "init" → init_agent.py
48
+
49
+ └── everything else → runner.py
50
+
51
+
52
+ subprocess (stdout+stderr merged)
53
+
54
+
55
+ stripper.py
56
+
57
+ ├── loads rules/<cmd>.yaml (or no rule → pass-through)
58
+ ├── applies strip/keep logic line-by-line
59
+ └── computes token savings
60
+
61
+
62
+ print output + ✂️ summary line
63
+ ```
64
+
65
+ **Command detection:**
66
+ - Single-word commands: `tidy pytest tests/` → rule key `pytest`
67
+ - Two-word subcommands: `tidy git status` → rule key `git_status`, `tidy docker logs` → `docker_logs`
68
+ - No matching rule → pass-through: run command, return output as-is, still print summary line (0% reduction)
69
+
70
+ ---
71
+
72
+ ## YAML Rule Schema
73
+
74
+ Each file in `tidyout/rules/` follows this schema:
75
+
76
+ ```yaml
77
+ strip:
78
+ - pattern: "regex or string"
79
+ type: regex | prefix | exact | contains
80
+
81
+ keep:
82
+ - pattern: "..."
83
+ type: regex | prefix | exact | contains
84
+ ```
85
+
86
+ **Evaluation order per line:**
87
+ 1. Matches any `keep` pattern → kept (keep always wins)
88
+ 2. Matches any `strip` pattern → dropped
89
+ 3. Otherwise → kept
90
+
91
+ **Special transforms** (reformatting, not just filtering) for `ls`, `git_log`, `docker_ps`, `git_status` are implemented as post-processing hooks in `stripper.py`, keyed by command name.
92
+
93
+ ---
94
+
95
+ ## Components
96
+
97
+ | File | Responsibility |
98
+ |---|---|
99
+ | `cli.py` | Parse `sys.argv`, detect `init`, dispatch to runner or init_agent |
100
+ | `runner.py` | `subprocess.run` with merged stdout+stderr, returns raw output string |
101
+ | `stripper.py` | Load rule YAML, apply strip/keep, run transform hooks, calculate savings |
102
+ | `init_agent.py` | Detect/create CLAUDE.md / .cursorrules, append tidyout usage block |
103
+ | `rules/*.yaml` | One file per command, declarative strip/keep patterns |
104
+
105
+ ---
106
+
107
+ ## Rules Inventory
108
+
109
+ | Rule file | Command | Typical savings |
110
+ |---|---|---|
111
+ | `pytest.yaml` | `pytest` | ~80% |
112
+ | `git_status.yaml` | `git status` | ~60% |
113
+ | `git_log.yaml` | `git log` | ~50% |
114
+ | `ls.yaml` | `ls` | ~70% |
115
+ | `find.yaml` | `find` | ~40% |
116
+ | `tree.yaml` | `tree` | ~50% |
117
+ | `pip_install.yaml` | `pip install` | ~90% |
118
+ | `npm_install.yaml` | `npm install` | ~85% |
119
+ | `docker_logs.yaml` | `docker logs` | ~70% |
120
+ | `docker_ps.yaml` | `docker ps` | ~60% |
121
+ | `jest.yaml` | `jest` | ~80% |
122
+ | `make.yaml` | `make` | ~40% |
123
+ | `cargo_build.yaml` | `cargo build` | ~75% |
124
+
125
+ ---
126
+
127
+ ## `tidy init` Behavior
128
+
129
+ - Detects `CLAUDE.md` in CWD → appends tidyout usage block
130
+ - Detects `.cursorrules` in CWD → appends tidyout usage block
131
+ - Neither exists → creates `CLAUDE.md` with the block
132
+ - Prints exactly what action was taken
133
+
134
+ **Injected block:**
135
+ ```markdown
136
+ ## Token Optimization with tidyout
137
+ Always prefix the following commands with `tidy` when running in terminal:
138
+ pytest, npm test, jest, rspec, git status, git log, ls, find, tree,
139
+ pip install, npm install, docker logs, docker ps, make, cargo build
140
+ This reduces token usage by 60-97% on command outputs.
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Token Savings Calculation
146
+
147
+ ```python
148
+ original_chars = len(original_output)
149
+ compressed_chars = len(compressed_output)
150
+ saved_tokens = (original_chars - compressed_chars) // 4
151
+ percent = int((1 - compressed_chars / original_chars) * 100) if original_chars else 0
152
+ ```
153
+
154
+ `1 token ≈ 4 characters` is the standard cross-industry rule of thumb (from OpenAI tokenizer research, also used by Anthropic). Accurate enough for an estimate.
155
+
156
+ ---
157
+
158
+ ## Testing
159
+
160
+ - `tests/test_stripper.py` — unit tests per rule: feed known raw output string, assert compressed output matches expected
161
+ - `tests/test_cli.py` — integration tests for `tidy init`: verify file creation/appending with correct content
162
+
163
+ ---
164
+
165
+ ## Packaging & Publishing
166
+
167
+ - `pyproject.toml`: package `tidyout`, version `0.1.0`, entry point `tidy = tidyout.cli:main`, Python `>=3.9`
168
+ - GitHub Actions workflow at `.github/workflows/publish.yml`: triggers on `v*.*.*` tags, runs tests, builds, publishes to PyPI via `PYPI_API_TOKEN` secret
169
+ - GitHub repo: `sathish-2000/tidyout`
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "tidyout-cli"
7
+ version = "0.1.0"
8
+ description = "CLI wrapper that compresses command output for AI agents, reducing token usage by 60-97%"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ keywords = ["cli", "ai", "tokens", "compression", "developer-tools"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Environment :: Console",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Software Development :: Libraries :: Python Modules",
24
+ "Topic :: Utilities",
25
+ ]
26
+ dependencies = [
27
+ "pyyaml>=6.0",
28
+ ]
29
+
30
+ [project.scripts]
31
+ tidy = "tidyout.cli:main"
32
+
33
+ [project.optional-dependencies]
34
+ dev = [
35
+ "pytest>=7.0",
36
+ ]
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["tidyout"]
40
+
41
+ [tool.pytest.ini_options]
42
+ testpaths = ["tests"]
@@ -0,0 +1,98 @@
1
+ import os
2
+ import pytest
3
+ from pathlib import Path
4
+
5
+ from tidyout.init_agent import run_init, TIDYOUT_BLOCK
6
+
7
+
8
+ def test_init_creates_claude_md_when_neither_exists(tmp_path):
9
+ orig = os.getcwd()
10
+ os.chdir(tmp_path)
11
+ try:
12
+ run_init()
13
+ claude_md = tmp_path / "CLAUDE.md"
14
+ assert claude_md.exists()
15
+ content = claude_md.read_text()
16
+ assert "tidyout" in content
17
+ assert "tidy" in content
18
+ finally:
19
+ os.chdir(orig)
20
+
21
+
22
+ def test_init_appends_to_existing_claude_md(tmp_path):
23
+ orig = os.getcwd()
24
+ os.chdir(tmp_path)
25
+ try:
26
+ claude_md = tmp_path / "CLAUDE.md"
27
+ claude_md.write_text("# Existing content\n")
28
+ run_init()
29
+ content = claude_md.read_text()
30
+ assert "# Existing content" in content
31
+ assert "tidyout" in content
32
+ finally:
33
+ os.chdir(orig)
34
+
35
+
36
+ def test_init_does_not_create_new_claude_md_when_cursorrules_exists(tmp_path):
37
+ orig = os.getcwd()
38
+ os.chdir(tmp_path)
39
+ try:
40
+ cursorrules = tmp_path / ".cursorrules"
41
+ cursorrules.write_text("# Cursor rules\n")
42
+ run_init()
43
+ assert not (tmp_path / "CLAUDE.md").exists()
44
+ assert "tidyout" in cursorrules.read_text()
45
+ finally:
46
+ os.chdir(orig)
47
+
48
+
49
+ def test_init_appends_to_cursorrules(tmp_path):
50
+ orig = os.getcwd()
51
+ os.chdir(tmp_path)
52
+ try:
53
+ cursorrules = tmp_path / ".cursorrules"
54
+ cursorrules.write_text("# Cursor rules\n")
55
+ run_init()
56
+ content = cursorrules.read_text()
57
+ assert "# Cursor rules" in content
58
+ assert "tidyout" in content
59
+ finally:
60
+ os.chdir(orig)
61
+
62
+
63
+ def test_init_prefers_claude_md_over_cursorrules(tmp_path):
64
+ orig = os.getcwd()
65
+ os.chdir(tmp_path)
66
+ try:
67
+ claude_md = tmp_path / "CLAUDE.md"
68
+ cursorrules = tmp_path / ".cursorrules"
69
+ claude_md.write_text("# Claude\n")
70
+ cursorrules.write_text("# Cursor\n")
71
+ run_init()
72
+ assert "tidyout" in claude_md.read_text()
73
+ assert "tidyout" not in cursorrules.read_text()
74
+ finally:
75
+ os.chdir(orig)
76
+
77
+
78
+ def test_init_block_contains_key_commands(tmp_path):
79
+ orig = os.getcwd()
80
+ os.chdir(tmp_path)
81
+ try:
82
+ run_init()
83
+ content = (tmp_path / "CLAUDE.md").read_text()
84
+ for cmd in ["pytest", "git status", "git log", "docker logs", "npm install"]:
85
+ assert cmd in content
86
+ finally:
87
+ os.chdir(orig)
88
+
89
+
90
+ def test_init_prints_action(tmp_path, capsys):
91
+ orig = os.getcwd()
92
+ os.chdir(tmp_path)
93
+ try:
94
+ run_init()
95
+ out = capsys.readouterr().out
96
+ assert "CLAUDE.md" in out
97
+ finally:
98
+ os.chdir(orig)