greedy-token 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stanislav Vasenkov
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.
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: greedy-token
3
+ Version: 0.2.1
4
+ Summary: Route dev tasks through tool → python → ollama → RAG before escalating to Cursor
5
+ Author: Stanislav Vasenkov
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/svasenkov/greedy-token
8
+ Project-URL: Repository, https://github.com/svasenkov/greedy-token
9
+ Keywords: cli,llm,cursor,token-optimization,rag,ollama
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: PyYAML>=6.0
14
+ Requires-Dist: tiktoken>=0.7
15
+ Dynamic: license-file
16
+
17
+ # greedy-token
18
+
19
+ Route dev tasks through **tool → python → ollama → RAG** before escalating to Cursor/Claude.
20
+
21
+ ```
22
+ Your task → greedy-token → rg/jq | scripts | Ollama | docs/rag | Cursor
23
+ ```
24
+
25
+ ## Cursor vs greedy-token (token comparison)
26
+
27
+ Measured with `greedy-token audit-context` and `greedy-token estimate` against [zero-design-system](https://github.com/svasenkov/zero-design-system) (tiktoken `cl100k_base`; order of magnitude, not API billing).
28
+
29
+ ### Cursor context overhead (every new chat)
30
+
31
+ | Context | Tokens | Charged when |
32
+ |---------|-------:|--------------|
33
+ | Always-on rules (`.cursor/rules/*.mdc`) | 2,524 | every chat |
34
+ | Skills on disk (`.cursor/skills/*/SKILL.md`) | 26,386 | if agent loads skill |
35
+ | Sampled docs (`CONTEXT.md`, `migration-prompts.md`) | 3,349 | if referenced |
36
+ | **Sampled set total** | **32,259** | full agent context |
37
+ | Naive agent baseline (`rules + 6k overhead + task`) | ~8,530 | default Cursor path |
38
+
39
+ Rules ≥ 1024 tokens → stable prefix is cache-friendly for Claude API prompt caching.
40
+
41
+ ### Task routing: naive Cursor vs greedy-token
42
+
43
+ | Task | Naive Cursor | greedy-token route | Est. tokens | Saved vs Cursor | Savings | Command |
44
+ |------|-------------:|--------------------|------------:|----------------:|--------:|---------|
45
+ | `какой -D flag для baseUrl в e2e config` | ~8,534 | **rag** (95%) | 1,810 | ~6,724 | ~79% | `greedy-token rag "baseUrl -D flag"` |
46
+ | `ADR 002 baseUrl pattern` | ~8,530 | **rag** (61%) | 1,806 | ~6,724 | ~79% | `greedy-token rag "ADR 002 baseUrl"` |
47
+ | `find baseUrl in e2e properties` | ~8,532 | **tool** (59%) | 0 | ~8,532 | ~100% | `greedy-token run "…" --execute` → `rg` |
48
+ | `sync phase-manifest и skills-map` | ~8,532 | **python** (65%) | 0 | ~8,532 | ~100% | `greedy-token scripts --run check-meta-sync --execute` |
49
+ | `rsync template-project в monorepo` | ~8,533 | **python** (60%) | 0 | ~8,533 | ~100% | dry-run script; run manually |
50
+ | `batch inventory template-project` | ~8,532 | **ollama** (66%) | 0 cloud | ~8,532 | ~100% cloud | `scripts/ollama/batch-inventory.sh` (local LLM) |
51
+ | `refactor header layout and wire nav links` | ~8,535 | **cursor** (82%) | 8,535 | 0 | — | new Cursor chat + skill from `docs/skills-map.md` |
52
+
53
+ **Tier order:** `tool (rg/jq) → python → ollama → rag → cursor` — first match wins. Ollama tier is skipped when unavailable.
54
+
55
+ **Takeaway:** lookup / search / sync / bulk tasks save **~6.7k–8.5k tokens per request**; wiring and architecture correctly stay on Cursor.
56
+
57
+ ```bash
58
+ greedy-token audit-context # your workspace overhead
59
+ greedy-token estimate "your task here" # route + savings before opening a chat
60
+ ```
61
+
62
+ Install once, point at your workspace root, route every task through the cheapest tier that can handle it.
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ pip install greedy-token
68
+ # or editable: pip install -e .
69
+ # or from git: pip install git+https://github.com/svasenkov/greedy-token.git
70
+ ```
71
+
72
+ `tiktoken` (exact BPE counts via `cl100k_base`) is a required dependency. If install fails
73
+ on an unsupported platform, use a Python version with a prebuilt `tiktoken` wheel or install
74
+ from source with a Rust toolchain.
75
+
76
+ ### PyPI publish (maintainer)
77
+
78
+ 1. Create project **greedy-token** on [pypi.org](https://pypi.org/manage/projects/)
79
+ 2. Add trusted publisher: Owner `svasenkov`, repo `greedy-token`, workflow `publish.yml`
80
+ 3. Publish: GitHub → Releases → re-run workflow or new tag
81
+
82
+ ## Workspace root
83
+
84
+ `greedy-token` runs against a project directory (monorepo, app repo, etc.):
85
+
86
+ ```bash
87
+ export GREEDY_TOKEN_ROOT=/path/to/your-workspace
88
+ ```
89
+
90
+ Auto-detect works when the workspace has `docs/phase-manifest.json` and `scripts/check-meta-sync.sh` (e.g. [zero-design-system](https://github.com/svasenkov/zero-design-system)). Otherwise set `GREEDY_TOKEN_ROOT` explicitly.
91
+
92
+ ## Commands
93
+
94
+ | Command | Purpose |
95
+ |---------|---------|
96
+ | `greedy-token route "…"` | Recommend: tool \| python \| ollama \| rag \| cursor + scoring |
97
+ | `greedy-token estimate "…"` | Token-aware estimate: complexity, est_tokens, tier scan |
98
+ | `greedy-token run "…" [--execute]` | Route + dry-run / **read-only** execute |
99
+ | `greedy-token scripts --list` | List workspace script wrappers |
100
+ | `greedy-token scripts --run ID [--execute]` | Dry-run / execute read-only wrapper |
101
+ | `greedy-token audit-context` | Size of always-on rules/skills (tokens) |
102
+ | `greedy-token tokens PATH…` | Count tokens in files/directories |
103
+ | `greedy-token rag QUERY` | Search chunks in `docs/rag/` |
104
+ | `greedy-token compress` | Short prompt version (stdin; `--ollama` for LLM) |
105
+
106
+ ## Tier order
107
+
108
+ ```
109
+ tool (rg/jq) → python → ollama → rag → cursor
110
+ ```
111
+
112
+ First matching tier wins. Ollama is **optional** — if unavailable, the tier is skipped.
113
+
114
+ ## Examples
115
+
116
+ ```bash
117
+ greedy-token route "find baseUrl"
118
+ # → tool (rg)
119
+
120
+ greedy-token estimate "refactor header layout"
121
+ # → cursor, complexity=high
122
+
123
+ greedy-token route "batch inventory template-project"
124
+ # → ollama
125
+
126
+ greedy-token route "sync phase-manifest and skills-map"
127
+ # → python
128
+
129
+ greedy-token route "ADR 002 baseUrl pattern"
130
+ # → rag
131
+ ```
132
+
133
+ ## Environment
134
+
135
+ | Var | Default |
136
+ |-----|---------|
137
+ | `GREEDY_TOKEN_ROOT` | auto-detect or required |
138
+ | `OLLAMA_URL` | `http://localhost:11434` |
139
+ | `OLLAMA_MODEL` | `qwen2.5-coder:14b` |
140
+
141
+ ## `--execute`
142
+
143
+ Read-only only: `rg`, `jq`, `check-meta-sync.sh`. Rsync/migrate/ollama — dry-run; run manually.
144
+
145
+ ## Route config
146
+
147
+ `src/greedy_token/config/routes.yaml` — customize patterns and commands for your workspace.
148
+
149
+ ## License
150
+
151
+ MIT
@@ -0,0 +1,135 @@
1
+ # greedy-token
2
+
3
+ Route dev tasks through **tool → python → ollama → RAG** before escalating to Cursor/Claude.
4
+
5
+ ```
6
+ Your task → greedy-token → rg/jq | scripts | Ollama | docs/rag | Cursor
7
+ ```
8
+
9
+ ## Cursor vs greedy-token (token comparison)
10
+
11
+ Measured with `greedy-token audit-context` and `greedy-token estimate` against [zero-design-system](https://github.com/svasenkov/zero-design-system) (tiktoken `cl100k_base`; order of magnitude, not API billing).
12
+
13
+ ### Cursor context overhead (every new chat)
14
+
15
+ | Context | Tokens | Charged when |
16
+ |---------|-------:|--------------|
17
+ | Always-on rules (`.cursor/rules/*.mdc`) | 2,524 | every chat |
18
+ | Skills on disk (`.cursor/skills/*/SKILL.md`) | 26,386 | if agent loads skill |
19
+ | Sampled docs (`CONTEXT.md`, `migration-prompts.md`) | 3,349 | if referenced |
20
+ | **Sampled set total** | **32,259** | full agent context |
21
+ | Naive agent baseline (`rules + 6k overhead + task`) | ~8,530 | default Cursor path |
22
+
23
+ Rules ≥ 1024 tokens → stable prefix is cache-friendly for Claude API prompt caching.
24
+
25
+ ### Task routing: naive Cursor vs greedy-token
26
+
27
+ | Task | Naive Cursor | greedy-token route | Est. tokens | Saved vs Cursor | Savings | Command |
28
+ |------|-------------:|--------------------|------------:|----------------:|--------:|---------|
29
+ | `какой -D flag для baseUrl в e2e config` | ~8,534 | **rag** (95%) | 1,810 | ~6,724 | ~79% | `greedy-token rag "baseUrl -D flag"` |
30
+ | `ADR 002 baseUrl pattern` | ~8,530 | **rag** (61%) | 1,806 | ~6,724 | ~79% | `greedy-token rag "ADR 002 baseUrl"` |
31
+ | `find baseUrl in e2e properties` | ~8,532 | **tool** (59%) | 0 | ~8,532 | ~100% | `greedy-token run "…" --execute` → `rg` |
32
+ | `sync phase-manifest и skills-map` | ~8,532 | **python** (65%) | 0 | ~8,532 | ~100% | `greedy-token scripts --run check-meta-sync --execute` |
33
+ | `rsync template-project в monorepo` | ~8,533 | **python** (60%) | 0 | ~8,533 | ~100% | dry-run script; run manually |
34
+ | `batch inventory template-project` | ~8,532 | **ollama** (66%) | 0 cloud | ~8,532 | ~100% cloud | `scripts/ollama/batch-inventory.sh` (local LLM) |
35
+ | `refactor header layout and wire nav links` | ~8,535 | **cursor** (82%) | 8,535 | 0 | — | new Cursor chat + skill from `docs/skills-map.md` |
36
+
37
+ **Tier order:** `tool (rg/jq) → python → ollama → rag → cursor` — first match wins. Ollama tier is skipped when unavailable.
38
+
39
+ **Takeaway:** lookup / search / sync / bulk tasks save **~6.7k–8.5k tokens per request**; wiring and architecture correctly stay on Cursor.
40
+
41
+ ```bash
42
+ greedy-token audit-context # your workspace overhead
43
+ greedy-token estimate "your task here" # route + savings before opening a chat
44
+ ```
45
+
46
+ Install once, point at your workspace root, route every task through the cheapest tier that can handle it.
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pip install greedy-token
52
+ # or editable: pip install -e .
53
+ # or from git: pip install git+https://github.com/svasenkov/greedy-token.git
54
+ ```
55
+
56
+ `tiktoken` (exact BPE counts via `cl100k_base`) is a required dependency. If install fails
57
+ on an unsupported platform, use a Python version with a prebuilt `tiktoken` wheel or install
58
+ from source with a Rust toolchain.
59
+
60
+ ### PyPI publish (maintainer)
61
+
62
+ 1. Create project **greedy-token** on [pypi.org](https://pypi.org/manage/projects/)
63
+ 2. Add trusted publisher: Owner `svasenkov`, repo `greedy-token`, workflow `publish.yml`
64
+ 3. Publish: GitHub → Releases → re-run workflow or new tag
65
+
66
+ ## Workspace root
67
+
68
+ `greedy-token` runs against a project directory (monorepo, app repo, etc.):
69
+
70
+ ```bash
71
+ export GREEDY_TOKEN_ROOT=/path/to/your-workspace
72
+ ```
73
+
74
+ Auto-detect works when the workspace has `docs/phase-manifest.json` and `scripts/check-meta-sync.sh` (e.g. [zero-design-system](https://github.com/svasenkov/zero-design-system)). Otherwise set `GREEDY_TOKEN_ROOT` explicitly.
75
+
76
+ ## Commands
77
+
78
+ | Command | Purpose |
79
+ |---------|---------|
80
+ | `greedy-token route "…"` | Recommend: tool \| python \| ollama \| rag \| cursor + scoring |
81
+ | `greedy-token estimate "…"` | Token-aware estimate: complexity, est_tokens, tier scan |
82
+ | `greedy-token run "…" [--execute]` | Route + dry-run / **read-only** execute |
83
+ | `greedy-token scripts --list` | List workspace script wrappers |
84
+ | `greedy-token scripts --run ID [--execute]` | Dry-run / execute read-only wrapper |
85
+ | `greedy-token audit-context` | Size of always-on rules/skills (tokens) |
86
+ | `greedy-token tokens PATH…` | Count tokens in files/directories |
87
+ | `greedy-token rag QUERY` | Search chunks in `docs/rag/` |
88
+ | `greedy-token compress` | Short prompt version (stdin; `--ollama` for LLM) |
89
+
90
+ ## Tier order
91
+
92
+ ```
93
+ tool (rg/jq) → python → ollama → rag → cursor
94
+ ```
95
+
96
+ First matching tier wins. Ollama is **optional** — if unavailable, the tier is skipped.
97
+
98
+ ## Examples
99
+
100
+ ```bash
101
+ greedy-token route "find baseUrl"
102
+ # → tool (rg)
103
+
104
+ greedy-token estimate "refactor header layout"
105
+ # → cursor, complexity=high
106
+
107
+ greedy-token route "batch inventory template-project"
108
+ # → ollama
109
+
110
+ greedy-token route "sync phase-manifest and skills-map"
111
+ # → python
112
+
113
+ greedy-token route "ADR 002 baseUrl pattern"
114
+ # → rag
115
+ ```
116
+
117
+ ## Environment
118
+
119
+ | Var | Default |
120
+ |-----|---------|
121
+ | `GREEDY_TOKEN_ROOT` | auto-detect or required |
122
+ | `OLLAMA_URL` | `http://localhost:11434` |
123
+ | `OLLAMA_MODEL` | `qwen2.5-coder:14b` |
124
+
125
+ ## `--execute`
126
+
127
+ Read-only only: `rg`, `jq`, `check-meta-sync.sh`. Rsync/migrate/ollama — dry-run; run manually.
128
+
129
+ ## Route config
130
+
131
+ `src/greedy_token/config/routes.yaml` — customize patterns and commands for your workspace.
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "greedy-token"
7
+ version = "0.2.1"
8
+ description = "Route dev tasks through tool → python → ollama → RAG before escalating to Cursor"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Stanislav Vasenkov" }]
13
+ keywords = ["cli", "llm", "cursor", "token-optimization", "rag", "ollama"]
14
+ dependencies = [
15
+ "PyYAML>=6.0",
16
+ "tiktoken>=0.7",
17
+ ]
18
+
19
+ [project.scripts]
20
+ greedy-token = "greedy_token.cli:main"
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/svasenkov/greedy-token"
24
+ Repository = "https://github.com/svasenkov/greedy-token"
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["src"]
28
+
29
+ [tool.setuptools.package-data]
30
+ greedy_token = ["config/*.yaml"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Greedy-token: route dev tasks before expensive LLM calls."""
2
+
3
+ __version__ = "0.2.0"
@@ -0,0 +1,4 @@
1
+ from greedy_token.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,205 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+
6
+ from greedy_token.context_audit import audit_context, render_audit
7
+ from greedy_token.estimator import estimate_task, format_estimate
8
+ from greedy_token.executors import execute_plan, plan_run
9
+ from greedy_token.paths import find_monorepo_root
10
+ from greedy_token.prompt_compress import compress_prompt, format_dual
11
+ from greedy_token.rag_search import format_hits, search_rag
12
+ from greedy_token.router import format_decision, route_task
13
+ from greedy_token.tokens import TokenEstimate, collect_paths, count_files, format_size_table
14
+ from greedy_token.wrappers import WRAPPERS, ollama_status_line, resolve_wrapper_command
15
+
16
+
17
+ def cmd_route(args: argparse.Namespace) -> int:
18
+ root = find_monorepo_root()
19
+ decision = route_task(args.task, root)
20
+ print(format_decision(decision, args.task, root))
21
+ return 0
22
+
23
+
24
+ def cmd_estimate(args: argparse.Namespace) -> int:
25
+ root = find_monorepo_root()
26
+ estimate = estimate_task(args.task, root)
27
+ print(format_estimate(estimate, args.task, root))
28
+ return 0
29
+
30
+
31
+ def cmd_run(args: argparse.Namespace) -> int:
32
+ root = find_monorepo_root()
33
+ decision = route_task(args.task, root)
34
+ plan = plan_run(decision, args.task, root)
35
+ print(f"Route: {decision.target} ({decision.route_id})")
36
+ print(f"Complexity: {decision.complexity} Est. tokens: {decision.est_tokens:,}")
37
+ print()
38
+ if args.execute:
39
+ code, out = execute_plan(plan)
40
+ print(out)
41
+ return code
42
+ print(plan.dry_run_output)
43
+ if plan.command:
44
+ if plan.executable:
45
+ print("\n(read-only — add --execute to run)")
46
+ else:
47
+ print("\n(not read-only — dry-run only)")
48
+ return 0
49
+
50
+
51
+ def cmd_audit_context(_: argparse.Namespace) -> int:
52
+ items = audit_context()
53
+ print(render_audit(items))
54
+ return 0
55
+
56
+
57
+ def cmd_tokens(args: argparse.Namespace) -> int:
58
+ root = find_monorepo_root()
59
+ paths = collect_paths(args.paths, root)
60
+ if not paths:
61
+ print("No files found.", file=sys.stderr)
62
+ return 1
63
+ estimates = count_files(paths)
64
+ rows = []
65
+ total_chars = 0
66
+ total_tokens = 0
67
+ method = "heuristic/4"
68
+ for p, est in zip(paths, estimates):
69
+ rel = str(p.relative_to(root)) if p.is_relative_to(root) else str(p)
70
+ rows.append((rel, est))
71
+ total_chars += est.chars
72
+ total_tokens += est.tokens
73
+ method = est.method
74
+ rows.sort(key=lambda r: -r[1].tokens)
75
+ total = TokenEstimate(tokens=total_tokens, chars=total_chars, method=method)
76
+ print(format_size_table(rows, total))
77
+ return 0
78
+
79
+
80
+ def cmd_rag(args: argparse.Namespace) -> int:
81
+ root = find_monorepo_root()
82
+ domains = args.domain.split(",") if args.domain else None
83
+ hits = search_rag(args.query, root, domains=domains, limit=args.limit)
84
+ print(format_hits(args.query, hits))
85
+ return 0
86
+
87
+
88
+ def cmd_compress(args: argparse.Namespace) -> int:
89
+ text = sys.stdin.read()
90
+ if not text.strip():
91
+ print("Read prompt from stdin.", file=sys.stderr)
92
+ return 1
93
+ short = compress_prompt(text, use_ollama=args.ollama)
94
+ if args.raw:
95
+ print(short)
96
+ else:
97
+ print(format_dual(text, short))
98
+ return 0
99
+
100
+
101
+ def cmd_scripts(args: argparse.Namespace) -> int:
102
+ root = find_monorepo_root()
103
+ if args.list:
104
+ lines = ["Script wrappers (scripts/ollama | migrate | check-meta-sync):", ""]
105
+ for wrapper in WRAPPERS.values():
106
+ ro = "read-only" if wrapper.read_only else "writes"
107
+ oll = " +ollama" if wrapper.requires_ollama else ""
108
+ lines.append(f" {wrapper.id:<20} [{wrapper.category}] {ro}{oll}")
109
+ lines.append(f" {wrapper.path}")
110
+ if wrapper.note:
111
+ lines.append(f" {wrapper.note}")
112
+ lines.append("")
113
+ lines.append(ollama_status_line())
114
+ print("\n".join(lines))
115
+ return 0
116
+ if args.run:
117
+ try:
118
+ cmd = resolve_wrapper_command(args.run, root, extra_args=args.args or "")
119
+ except (KeyError, FileNotFoundError) as exc:
120
+ print(exc, file=sys.stderr)
121
+ return 1
122
+ wrapper = WRAPPERS[args.run]
123
+ if args.execute:
124
+ if not wrapper.read_only:
125
+ print(
126
+ f"Refusing --execute: {args.run} is not read-only.",
127
+ file=sys.stderr,
128
+ )
129
+ return 1
130
+ import subprocess
131
+
132
+ proc = subprocess.run(cmd, shell=True)
133
+ return proc.returncode
134
+ print(cmd)
135
+ if wrapper.read_only:
136
+ print("\n(read-only — add --execute to run)")
137
+ else:
138
+ print("\n(not read-only — dry-run only)")
139
+ return 0
140
+ print("Use scripts --list or scripts --run ID", file=sys.stderr)
141
+ return 1
142
+
143
+
144
+ def build_parser() -> argparse.ArgumentParser:
145
+ p = argparse.ArgumentParser(
146
+ prog="greedy-token",
147
+ description="Task orchestrator: tool | Python | Ollama | RAG | Cursor",
148
+ )
149
+ sub = p.add_subparsers(dest="command", required=True)
150
+
151
+ r = sub.add_parser("route", help="Recommend executor for a task")
152
+ r.add_argument("task", help="Natural language task description")
153
+ r.set_defaults(func=cmd_route)
154
+
155
+ est = sub.add_parser("estimate", help="Token-aware route estimate with tier scan")
156
+ est.add_argument("task", help="Task description")
157
+ est.set_defaults(func=cmd_estimate)
158
+
159
+ run = sub.add_parser("run", help="Route and show/run command")
160
+ run.add_argument("task", help="Task description")
161
+ run.add_argument(
162
+ "--execute",
163
+ action="store_true",
164
+ help="Execute read-only tool/python commands only",
165
+ )
166
+ run.set_defaults(func=cmd_run)
167
+
168
+ sub.add_parser("audit-context", help="Token audit of rules/skills").set_defaults(
169
+ func=cmd_audit_context
170
+ )
171
+
172
+ t = sub.add_parser("tokens", help="Count tokens in paths")
173
+ t.add_argument("paths", nargs="+", help="Files or directories")
174
+ t.set_defaults(func=cmd_tokens)
175
+
176
+ rag = sub.add_parser("rag", help="Search docs/rag chunks")
177
+ rag.add_argument("query", help="Search query")
178
+ rag.add_argument("--domain", help="Comma-separated domains filter")
179
+ rag.add_argument("--limit", type=int, default=5)
180
+ rag.set_defaults(func=cmd_rag)
181
+
182
+ c = sub.add_parser("compress", help="Short agent prompt from stdin")
183
+ c.add_argument("--ollama", action="store_true", help="Use local Ollama")
184
+ c.add_argument("--raw", action="store_true", help="Print short text only")
185
+ c.set_defaults(func=cmd_compress)
186
+
187
+ scr = sub.add_parser("scripts", help="Wrappers for monorepo scripts")
188
+ scr.add_argument("--list", action="store_true", help="List script wrappers")
189
+ scr.add_argument("--run", metavar="ID", help="Wrapper id (e.g. check-meta-sync)")
190
+ scr.add_argument("args", nargs="?", default="", help="Extra args for script")
191
+ scr.add_argument(
192
+ "--execute",
193
+ action="store_true",
194
+ help="Run read-only wrapper only",
195
+ )
196
+ scr.set_defaults(func=cmd_scripts)
197
+
198
+ return p
199
+
200
+
201
+ def main(argv: list[str] | None = None) -> None:
202
+ parser = build_parser()
203
+ args = parser.parse_args(argv)
204
+ code = args.func(args)
205
+ raise SystemExit(code)