tier-ctx 1.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.
- tier_ctx-1.2.1/LICENSE +21 -0
- tier_ctx-1.2.1/PKG-INFO +273 -0
- tier_ctx-1.2.1/README.md +252 -0
- tier_ctx-1.2.1/pyproject.toml +31 -0
- tier_ctx-1.2.1/setup.cfg +4 -0
- tier_ctx-1.2.1/src/tier/__init__.py +53 -0
- tier_ctx-1.2.1/src/tier/__main__.py +5 -0
- tier_ctx-1.2.1/src/tier/cache.py +325 -0
- tier_ctx-1.2.1/src/tier/cli.py +467 -0
- tier_ctx-1.2.1/src/tier/context.py +369 -0
- tier_ctx-1.2.1/src/tier/converter.py +451 -0
- tier_ctx-1.2.1/src/tier/doctor.py +215 -0
- tier_ctx-1.2.1/src/tier/installer.py +1482 -0
- tier_ctx-1.2.1/src/tier/lint.py +58 -0
- tier_ctx-1.2.1/src/tier/mcp/__init__.py +9 -0
- tier_ctx-1.2.1/src/tier/mcp/__main__.py +5 -0
- tier_ctx-1.2.1/src/tier/mcp/sdk_server.py +183 -0
- tier_ctx-1.2.1/src/tier/mcp/server.py +567 -0
- tier_ctx-1.2.1/src/tier/memory.py +171 -0
- tier_ctx-1.2.1/src/tier/models.py +202 -0
- tier_ctx-1.2.1/src/tier/parser.py +301 -0
- tier_ctx-1.2.1/src/tier/pipe.py +328 -0
- tier_ctx-1.2.1/src/tier/plugin.py +167 -0
- tier_ctx-1.2.1/src/tier/repo.py +1694 -0
- tier_ctx-1.2.1/src/tier/retrieval.py +385 -0
- tier_ctx-1.2.1/src/tier/tokens.py +294 -0
- tier_ctx-1.2.1/src/tier/ui.py +129 -0
- tier_ctx-1.2.1/src/tier_ctx.egg-info/PKG-INFO +273 -0
- tier_ctx-1.2.1/src/tier_ctx.egg-info/SOURCES.txt +34 -0
- tier_ctx-1.2.1/src/tier_ctx.egg-info/dependency_links.txt +1 -0
- tier_ctx-1.2.1/src/tier_ctx.egg-info/entry_points.txt +4 -0
- tier_ctx-1.2.1/src/tier_ctx.egg-info/requires.txt +14 -0
- tier_ctx-1.2.1/src/tier_ctx.egg-info/top_level.txt +1 -0
- tier_ctx-1.2.1/tests/test_installer.py +821 -0
- tier_ctx-1.2.1/tests/test_tier.py +2025 -0
- tier_ctx-1.2.1/tests/test_v110.py +837 -0
tier_ctx-1.2.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 000jd
|
|
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.
|
tier_ctx-1.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tier-ctx
|
|
3
|
+
Version: 1.2.1
|
|
4
|
+
Summary: TIER: layered context, repository graphs, code slicing, and agent memory
|
|
5
|
+
Author: 000jd
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Provides-Extra: ui
|
|
11
|
+
Requires-Dist: rich>=13.0; extra == "ui"
|
|
12
|
+
Provides-Extra: mcp
|
|
13
|
+
Requires-Dist: mcp>=2.0; extra == "mcp"
|
|
14
|
+
Provides-Extra: treesitter
|
|
15
|
+
Requires-Dist: tree-sitter-language-pack>=0.7.0; extra == "treesitter"
|
|
16
|
+
Provides-Extra: all
|
|
17
|
+
Requires-Dist: rich>=13.0; extra == "all"
|
|
18
|
+
Requires-Dist: mcp>=2.0; extra == "all"
|
|
19
|
+
Requires-Dist: tree-sitter-language-pack>=0.7.0; extra == "all"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
<div align="center">
|
|
23
|
+
|
|
24
|
+
# TIER
|
|
25
|
+
|
|
26
|
+
**Layered context for AI coding agents: a 4-layer document and repository format, a code graph
|
|
27
|
+
indexer, and a zero-dependency MCP server.**
|
|
28
|
+
|
|
29
|
+
[](.github/workflows/ci.yml)
|
|
30
|
+
[](https://pypi.org/project/tier-ctx/)
|
|
31
|
+
[](pyproject.toml)
|
|
32
|
+
[](LICENSE)
|
|
33
|
+
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
`tier` keeps an agent from loading whole repositories and documents into its context. Every
|
|
39
|
+
document carries a small always-loadable index, one-line abstracts, structured bodies with symbol
|
|
40
|
+
signatures and dependency edges, and exact source-line ranges it can fetch on demand. A hybrid
|
|
41
|
+
retriever routes a question to the right section, an incremental repository map keeps fingerprints
|
|
42
|
+
and line ranges current, and a reverse-dependency graph answers what an edit affects.
|
|
43
|
+
|
|
44
|
+
The core is pure Python standard library: `dependencies = []`, no network calls, no telemetry.
|
|
45
|
+
It runs entirely offline on Python 3.10 and later. Optional extras add terminal styling, the
|
|
46
|
+
official MCP SDK adapter, and Tree-sitter grammars.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
Zero-clone installs, in order of least commitment:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
uvx --from tier-ctx tier install # run the installer once, leave nothing installed
|
|
54
|
+
uv tool install tier-ctx # put `tier`, `tier-mcp`, and `tier-mcp-sdk` on PATH
|
|
55
|
+
pipx install tier-ctx # the same, where pipx is preferred
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Until the PyPI release is live, install from Git:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
uv tool install git+https://github.com/000jd/tier
|
|
62
|
+
uv tool install "tier-ctx[all] @ git+https://github.com/000jd/tier" # + rich UI, MCP SDK
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
From a checkout, inside a virtual environment:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
git clone https://github.com/000jd/tier && cd tier
|
|
69
|
+
python3 -m venv .venv && . .venv/bin/activate
|
|
70
|
+
pip install -e ".[all]"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> The distribution is named `tier-ctx`; the import package and console scripts are `tier`,
|
|
74
|
+
> `tier-mcp`, and `tier-mcp-sdk`. The bare PyPI name `tier` belongs to an unrelated package.
|
|
75
|
+
|
|
76
|
+
## Example
|
|
77
|
+
|
|
78
|
+
An end-to-end session on a small project — install once, then one call per question:
|
|
79
|
+
|
|
80
|
+
````console
|
|
81
|
+
$ tier install --clients claude-code --yes
|
|
82
|
+
Target: /home/me/myproject
|
|
83
|
+
Clients: claude-code
|
|
84
|
+
cache (.git/info/tier): /home/me/myproject/.git/info/tier/repo.tier (created)
|
|
85
|
+
Tokens: raw source 197 -> L0 130 (34.0% saved)
|
|
86
|
+
Instructions: /home/me/myproject/CLAUDE.md, /home/me/myproject/AGENTS.md
|
|
87
|
+
|
|
88
|
+
$ tier context . "how does the retry backoff work" --budget-tokens 800
|
|
89
|
+
## Context: how does the retry backoff work
|
|
90
|
+
|
|
91
|
+
@src-http-client.py src/http/client.py
|
|
92
|
+
? HTTP client with retry and backoff.; symbols: backoff_delay, request
|
|
93
|
+
|symbols[3]{kind,name,signature,lines}
|
|
94
|
+
function,backoff_delay,def backoff_delay(attempt: int) -> float,7-9
|
|
95
|
+
function,request,"def request(url: str, retries: int=DEFAULT_RETRIES)",12-19
|
|
96
|
+
function,_send,def _send(url: str),22-23
|
|
97
|
+
|calls[1]{caller,callee,module}
|
|
98
|
+
request,backoff_delay,.
|
|
99
|
+
|
|
100
|
+
@src-http-client.py#backoff_delay [L7-L9]
|
|
101
|
+
```python
|
|
102
|
+
def backoff_delay(attempt: int) -> float:
|
|
103
|
+
"""Exponential backoff, capped at 30 seconds."""
|
|
104
|
+
return min(2 ** attempt, 30.0)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Impact: @src-http-client.py#backoff_delay
|
|
108
|
+
Direct dependents: @src-api-client.py
|
|
109
|
+
Transitive dependents: @src-api-client.py
|
|
110
|
+
Symbol callers: src/http/client.py#request [12-19]
|
|
111
|
+
|
|
112
|
+
$ tier impact src/http/client.py#backoff_delay
|
|
113
|
+
Impact target: @src-http-client.py
|
|
114
|
+
Direct dependents: @src-api-client.py
|
|
115
|
+
Transitive dependents: @src-api-client.py
|
|
116
|
+
Dependencies: none
|
|
117
|
+
Transitive dependencies: none
|
|
118
|
+
Symbol: src/http/client.py#backoff_delay
|
|
119
|
+
Symbol callers: src/http/client.py#request [12-19]
|
|
120
|
+
Symbol calls: none
|
|
121
|
+
|
|
122
|
+
$ tier slice src/http/client.py backoff_delay
|
|
123
|
+
@src-http-client.py#backoff_delay [L7-L9]
|
|
124
|
+
```python
|
|
125
|
+
def backoff_delay(attempt: int) -> float:
|
|
126
|
+
"""Exponential backoff, capped at 30 seconds."""
|
|
127
|
+
return min(2 ** attempt, 30.0)
|
|
128
|
+
```
|
|
129
|
+
````
|
|
130
|
+
|
|
131
|
+
## How it works
|
|
132
|
+
|
|
133
|
+
TIER splits every document into four layers, and an agent reads only the ones it needs.
|
|
134
|
+
|
|
135
|
+
- **L0 — index.** Document metadata, global rules, and one line per section carrying that
|
|
136
|
+
section's token cost and rule count. Always loaded; a few hundred tokens.
|
|
137
|
+
- **L1 — abstracts.** A one-line `?` summary per section saying *when* to read it, folded into
|
|
138
|
+
the L0 index.
|
|
139
|
+
- **L2 — bodies.** Section contents; for codebases, a `|symbols` table with exact
|
|
140
|
+
`[start-end]` line ranges and `>` edges to local dependencies.
|
|
141
|
+
- **L3 — slices.** Exact source lines for one symbol, fetched by name.
|
|
142
|
+
|
|
143
|
+
`tier_context` collapses the whole loop into one call: resolve or build the repository index,
|
|
144
|
+
route the query with the hybrid retriever, match symbol names, then pack L2 bodies, L3 slices,
|
|
145
|
+
and a dependency summary until the token budget is spent. The granular commands remain available
|
|
146
|
+
when you need a specific section id or symbol.
|
|
147
|
+
|
|
148
|
+
## Architecture highlights
|
|
149
|
+
|
|
150
|
+
**Header-once tables and indented key/value trees.** A record set is a header plus exactly N CSV
|
|
151
|
+
rows, and nested data is a two-space-indented tree. Both keep the schema and drop the repeated
|
|
152
|
+
punctuation that JSON and Markdown tables spend tokens on.
|
|
153
|
+
|
|
154
|
+
**Deterministic line-start sigils.** Every line is classified by its first character: `%` header,
|
|
155
|
+
`@` section, `?` abstract, `!` rule, `=` key/value, `|` counted table, `>` reference, `$` command.
|
|
156
|
+
Fenced code, untrusted blocks, and table rows are opaque, so no sigil inside them is reinterpreted.
|
|
157
|
+
|
|
158
|
+
**Untrusted content guards.** Text from the outside world is wrapped in `<<<source #guard ...
|
|
159
|
+
>>>guard`. The guards are chosen so that the payload cannot contain its own terminator, which makes
|
|
160
|
+
embedded instructions, fake headers, and rule sigils inert data.
|
|
161
|
+
|
|
162
|
+
**Stateful session deduplication.** A section or source slice already delivered in the conversation
|
|
163
|
+
is replaced by a one-line stub on the next request. `sync` evicts exactly the sections whose files
|
|
164
|
+
changed, and their slices.
|
|
165
|
+
|
|
166
|
+
**Symbol-level blast radius.** Repository maps record `>` edges between modules and a `|calls`
|
|
167
|
+
table of caller/callee pairs. `tier impact` reports file-level importers and, given
|
|
168
|
+
`module#symbol`, the exact functions that call it. `tier_context` filters `|calls` tables to the
|
|
169
|
+
1-hop neighbourhood of the sections it returns; pass `include_calls="all"` to see every edge.
|
|
170
|
+
|
|
171
|
+
**Invisible repository cache.** The map is a derived artifact, so it lives in
|
|
172
|
+
`.git/info/tier/repo.tier` inside a worktree (which Git ignores) or in a user cache directory
|
|
173
|
+
outside it. `.git` is followed when it is a pointer file rather than a directory, so linked
|
|
174
|
+
worktrees and submodules resolve to their own git directory. A tracked `repo.tier` is still
|
|
175
|
+
honoured when one exists. Nothing is written into your working tree, so `git status` stays clean.
|
|
176
|
+
|
|
177
|
+
**Hybrid search with precomputed IDF.** BM25-style term scoring over split identifiers, a compact
|
|
178
|
+
synonym map, character 3-gram fallback for typos, and navigation priors. Document frequencies are
|
|
179
|
+
computed once per query, which keeps a search over 800 sections near 200 ms with no index to build.
|
|
180
|
+
|
|
181
|
+
## Performance
|
|
182
|
+
|
|
183
|
+
Measured with the `o200k_base` tokenizer. These are token counts and retrieval hit rates, not LLM
|
|
184
|
+
answer accuracy. Methodology and reproduction steps are in
|
|
185
|
+
[`benchmarks/README.md`](benchmarks/README.md).
|
|
186
|
+
|
|
187
|
+
| Workload | Measure | Result |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| Repository overview | raw source → full map → L0 → L0 + one module | 72,859 → 14,375 → 828 → 1,784 tokens (−98%) |
|
|
190
|
+
| End-to-end 4-layer workflow | index, find, L2, L3 slices, impact + sync | 5,003 tokens (−93.1%), 3/3 exact slices |
|
|
191
|
+
| 36-file polyglot repository | surgical L0 + target L2 + 7 slices | 3,008 vs 8,249 tokens (−64%), 116/116 symbols |
|
|
192
|
+
| 197-section blind retrieval | 15 queries | 15/15 hit@1, mean top-1 fetch 134 tokens |
|
|
193
|
+
|
|
194
|
+
[`benchmarks/BENCH.txt`](benchmarks/BENCH.txt) carries the full Track A–I breakdown, including the
|
|
195
|
+
record-encoding and nested-config tracks, the per-query retrieval logs, and the raw output behind
|
|
196
|
+
every row above.
|
|
197
|
+
|
|
198
|
+
## Supported clients
|
|
199
|
+
|
|
200
|
+
`tier install` configures 25 agent clients across JSON, TOML, YAML, and `SKILL.md` schemas. It
|
|
201
|
+
backs up every file it touches, merges instead of overwriting, supports `--dry-run` and
|
|
202
|
+
`--uninstall`, and validates the result with `tier doctor`.
|
|
203
|
+
|
|
204
|
+
<details>
|
|
205
|
+
<summary><b>Client list and config paths</b></summary>
|
|
206
|
+
|
|
207
|
+
| Client | Scope | Config |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| Claude Code | project / user | `.mcp.json` + `.claude/settings.json`, `~/.claude.json` |
|
|
210
|
+
| Claude Desktop | user | `claude_desktop_config.json` |
|
|
211
|
+
| Command Code | project / user | `.commandcode/mcp.json`, `~/.commandcode/mcp.json` |
|
|
212
|
+
| OpenCode | project / global | `opencode.json`, `~/.config/opencode/opencode.json` |
|
|
213
|
+
| OpenAI Codex CLI | project / user | `.codex/config.toml`, `~/.codex/config.toml` |
|
|
214
|
+
| Gemini CLI | project / user | `.gemini/settings.json` + `GEMINI.md` |
|
|
215
|
+
| Cursor | project / global | `.cursor/mcp.json`, `~/.cursor/mcp.json` |
|
|
216
|
+
| VS Code Copilot | project | `.vscode/mcp.json` |
|
|
217
|
+
| Windsurf | user | `~/.codeium/windsurf/mcp_config.json` |
|
|
218
|
+
| Roo Code / Cline | project | `.roo/mcp.json` |
|
|
219
|
+
| Trae | project | `.trae/mcp.json` |
|
|
220
|
+
| JetBrains Junie | project / user | `.junie/mcp/mcp.json`, `~/.junie/mcp/mcp.json` |
|
|
221
|
+
| Kiro | project | `.kiro/settings/mcp.json` |
|
|
222
|
+
| Warp | user | `~/.warp/.mcp.json` |
|
|
223
|
+
| Zed | user | `~/.config/zed/settings.json` (`context_servers`) |
|
|
224
|
+
| Continue.dev | project | `.continue/mcpServers/tier.yaml` |
|
|
225
|
+
| Agent Skills | project / user | `.agents/skills/tier/SKILL.md` |
|
|
226
|
+
|
|
227
|
+
Any other client can be configured with `--custom-config PATH --schema NAME`, and
|
|
228
|
+
`tier config-snippet` prints ready-to-copy JSON, TOML, YAML, and `SKILL.md` snippets plus native
|
|
229
|
+
CLI one-liners.
|
|
230
|
+
|
|
231
|
+
</details>
|
|
232
|
+
|
|
233
|
+
<details>
|
|
234
|
+
<summary><b>Optional extras</b></summary>
|
|
235
|
+
|
|
236
|
+
```sh
|
|
237
|
+
uv tool install "tier-ctx[ui] @ git+https://github.com/000jd/tier" # rich terminal UI
|
|
238
|
+
uv tool install "tier-ctx[mcp] @ git+https://github.com/000jd/tier" # official MCP SDK
|
|
239
|
+
uv tool install "tier-ctx[treesitter] @ git+https://github.com/000jd/tier" # multi-language AST
|
|
240
|
+
uv tool install "tier-ctx[all] @ git+https://github.com/000jd/tier"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
- `[ui]` adds `rich` rendering for `tier install` and `tier doctor`.
|
|
244
|
+
- `[mcp]` adds the official SDK adapter (`tier-mcp-sdk`), Streamable HTTP, and MCP Inspector.
|
|
245
|
+
- `[treesitter]` adds Tree-sitter grammars for TypeScript, JavaScript, Go, Rust, C, C++, Java,
|
|
246
|
+
and Ruby. Without it the stdlib extractors are used, and repository maps are byte-identical.
|
|
247
|
+
|
|
248
|
+
</details>
|
|
249
|
+
|
|
250
|
+
## Documentation
|
|
251
|
+
|
|
252
|
+
- [`docs/spec.md`](docs/spec.md) — the format, parser rules, and retrieval protocol
|
|
253
|
+
- [`ARCHITECTURE.md`](ARCHITECTURE.md) — module boundaries, layer flow, and invariants
|
|
254
|
+
- [`SKILL.md`](SKILL.md) — the canonical Agent Skill: entry point, CLI and MCP tables, working rules
|
|
255
|
+
- [`docs/cli.md`](docs/cli.md) — every command and flag
|
|
256
|
+
- [`docs/mcp.md`](docs/mcp.md) — the 12 MCP tools, one-turn workflow, and cache behaviour
|
|
257
|
+
- [`benchmarks/README.md`](benchmarks/README.md) — workload definitions and methodology
|
|
258
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — development setup and extension guides
|
|
259
|
+
- [`CHANGELOG.md`](CHANGELOG.md) — release history
|
|
260
|
+
|
|
261
|
+
## Limits
|
|
262
|
+
|
|
263
|
+
Only token counts, retrieval identifiers, and exact-slice coverage are measured — not LLM answer
|
|
264
|
+
accuracy. Claude's tokenizer is not public, so counts use OpenAI `o200k` via a persistent Node
|
|
265
|
+
worker when available, and a characters/4 estimate otherwise. `tier hook` treats its token budget
|
|
266
|
+
as a contract: it skips a payload only when it is provably within budget, otherwise it counts
|
|
267
|
+
exactly and verifies the output. `tier sync` trusts recorded mtime and size as a cache; use
|
|
268
|
+
`tier sync --rehash` after tooling that restores timestamps. JS/TS/Go/Rust symbol extraction is
|
|
269
|
+
heuristic without the `[treesitter]` extra.
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
MIT © 2026 000jd. See [LICENSE](LICENSE).
|
tier_ctx-1.2.1/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# TIER
|
|
4
|
+
|
|
5
|
+
**Layered context for AI coding agents: a 4-layer document and repository format, a code graph
|
|
6
|
+
indexer, and a zero-dependency MCP server.**
|
|
7
|
+
|
|
8
|
+
[](.github/workflows/ci.yml)
|
|
9
|
+
[](https://pypi.org/project/tier-ctx/)
|
|
10
|
+
[](pyproject.toml)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
`tier` keeps an agent from loading whole repositories and documents into its context. Every
|
|
18
|
+
document carries a small always-loadable index, one-line abstracts, structured bodies with symbol
|
|
19
|
+
signatures and dependency edges, and exact source-line ranges it can fetch on demand. A hybrid
|
|
20
|
+
retriever routes a question to the right section, an incremental repository map keeps fingerprints
|
|
21
|
+
and line ranges current, and a reverse-dependency graph answers what an edit affects.
|
|
22
|
+
|
|
23
|
+
The core is pure Python standard library: `dependencies = []`, no network calls, no telemetry.
|
|
24
|
+
It runs entirely offline on Python 3.10 and later. Optional extras add terminal styling, the
|
|
25
|
+
official MCP SDK adapter, and Tree-sitter grammars.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
Zero-clone installs, in order of least commitment:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
uvx --from tier-ctx tier install # run the installer once, leave nothing installed
|
|
33
|
+
uv tool install tier-ctx # put `tier`, `tier-mcp`, and `tier-mcp-sdk` on PATH
|
|
34
|
+
pipx install tier-ctx # the same, where pipx is preferred
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Until the PyPI release is live, install from Git:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
uv tool install git+https://github.com/000jd/tier
|
|
41
|
+
uv tool install "tier-ctx[all] @ git+https://github.com/000jd/tier" # + rich UI, MCP SDK
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
From a checkout, inside a virtual environment:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
git clone https://github.com/000jd/tier && cd tier
|
|
48
|
+
python3 -m venv .venv && . .venv/bin/activate
|
|
49
|
+
pip install -e ".[all]"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> The distribution is named `tier-ctx`; the import package and console scripts are `tier`,
|
|
53
|
+
> `tier-mcp`, and `tier-mcp-sdk`. The bare PyPI name `tier` belongs to an unrelated package.
|
|
54
|
+
|
|
55
|
+
## Example
|
|
56
|
+
|
|
57
|
+
An end-to-end session on a small project — install once, then one call per question:
|
|
58
|
+
|
|
59
|
+
````console
|
|
60
|
+
$ tier install --clients claude-code --yes
|
|
61
|
+
Target: /home/me/myproject
|
|
62
|
+
Clients: claude-code
|
|
63
|
+
cache (.git/info/tier): /home/me/myproject/.git/info/tier/repo.tier (created)
|
|
64
|
+
Tokens: raw source 197 -> L0 130 (34.0% saved)
|
|
65
|
+
Instructions: /home/me/myproject/CLAUDE.md, /home/me/myproject/AGENTS.md
|
|
66
|
+
|
|
67
|
+
$ tier context . "how does the retry backoff work" --budget-tokens 800
|
|
68
|
+
## Context: how does the retry backoff work
|
|
69
|
+
|
|
70
|
+
@src-http-client.py src/http/client.py
|
|
71
|
+
? HTTP client with retry and backoff.; symbols: backoff_delay, request
|
|
72
|
+
|symbols[3]{kind,name,signature,lines}
|
|
73
|
+
function,backoff_delay,def backoff_delay(attempt: int) -> float,7-9
|
|
74
|
+
function,request,"def request(url: str, retries: int=DEFAULT_RETRIES)",12-19
|
|
75
|
+
function,_send,def _send(url: str),22-23
|
|
76
|
+
|calls[1]{caller,callee,module}
|
|
77
|
+
request,backoff_delay,.
|
|
78
|
+
|
|
79
|
+
@src-http-client.py#backoff_delay [L7-L9]
|
|
80
|
+
```python
|
|
81
|
+
def backoff_delay(attempt: int) -> float:
|
|
82
|
+
"""Exponential backoff, capped at 30 seconds."""
|
|
83
|
+
return min(2 ** attempt, 30.0)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Impact: @src-http-client.py#backoff_delay
|
|
87
|
+
Direct dependents: @src-api-client.py
|
|
88
|
+
Transitive dependents: @src-api-client.py
|
|
89
|
+
Symbol callers: src/http/client.py#request [12-19]
|
|
90
|
+
|
|
91
|
+
$ tier impact src/http/client.py#backoff_delay
|
|
92
|
+
Impact target: @src-http-client.py
|
|
93
|
+
Direct dependents: @src-api-client.py
|
|
94
|
+
Transitive dependents: @src-api-client.py
|
|
95
|
+
Dependencies: none
|
|
96
|
+
Transitive dependencies: none
|
|
97
|
+
Symbol: src/http/client.py#backoff_delay
|
|
98
|
+
Symbol callers: src/http/client.py#request [12-19]
|
|
99
|
+
Symbol calls: none
|
|
100
|
+
|
|
101
|
+
$ tier slice src/http/client.py backoff_delay
|
|
102
|
+
@src-http-client.py#backoff_delay [L7-L9]
|
|
103
|
+
```python
|
|
104
|
+
def backoff_delay(attempt: int) -> float:
|
|
105
|
+
"""Exponential backoff, capped at 30 seconds."""
|
|
106
|
+
return min(2 ** attempt, 30.0)
|
|
107
|
+
```
|
|
108
|
+
````
|
|
109
|
+
|
|
110
|
+
## How it works
|
|
111
|
+
|
|
112
|
+
TIER splits every document into four layers, and an agent reads only the ones it needs.
|
|
113
|
+
|
|
114
|
+
- **L0 — index.** Document metadata, global rules, and one line per section carrying that
|
|
115
|
+
section's token cost and rule count. Always loaded; a few hundred tokens.
|
|
116
|
+
- **L1 — abstracts.** A one-line `?` summary per section saying *when* to read it, folded into
|
|
117
|
+
the L0 index.
|
|
118
|
+
- **L2 — bodies.** Section contents; for codebases, a `|symbols` table with exact
|
|
119
|
+
`[start-end]` line ranges and `>` edges to local dependencies.
|
|
120
|
+
- **L3 — slices.** Exact source lines for one symbol, fetched by name.
|
|
121
|
+
|
|
122
|
+
`tier_context` collapses the whole loop into one call: resolve or build the repository index,
|
|
123
|
+
route the query with the hybrid retriever, match symbol names, then pack L2 bodies, L3 slices,
|
|
124
|
+
and a dependency summary until the token budget is spent. The granular commands remain available
|
|
125
|
+
when you need a specific section id or symbol.
|
|
126
|
+
|
|
127
|
+
## Architecture highlights
|
|
128
|
+
|
|
129
|
+
**Header-once tables and indented key/value trees.** A record set is a header plus exactly N CSV
|
|
130
|
+
rows, and nested data is a two-space-indented tree. Both keep the schema and drop the repeated
|
|
131
|
+
punctuation that JSON and Markdown tables spend tokens on.
|
|
132
|
+
|
|
133
|
+
**Deterministic line-start sigils.** Every line is classified by its first character: `%` header,
|
|
134
|
+
`@` section, `?` abstract, `!` rule, `=` key/value, `|` counted table, `>` reference, `$` command.
|
|
135
|
+
Fenced code, untrusted blocks, and table rows are opaque, so no sigil inside them is reinterpreted.
|
|
136
|
+
|
|
137
|
+
**Untrusted content guards.** Text from the outside world is wrapped in `<<<source #guard ...
|
|
138
|
+
>>>guard`. The guards are chosen so that the payload cannot contain its own terminator, which makes
|
|
139
|
+
embedded instructions, fake headers, and rule sigils inert data.
|
|
140
|
+
|
|
141
|
+
**Stateful session deduplication.** A section or source slice already delivered in the conversation
|
|
142
|
+
is replaced by a one-line stub on the next request. `sync` evicts exactly the sections whose files
|
|
143
|
+
changed, and their slices.
|
|
144
|
+
|
|
145
|
+
**Symbol-level blast radius.** Repository maps record `>` edges between modules and a `|calls`
|
|
146
|
+
table of caller/callee pairs. `tier impact` reports file-level importers and, given
|
|
147
|
+
`module#symbol`, the exact functions that call it. `tier_context` filters `|calls` tables to the
|
|
148
|
+
1-hop neighbourhood of the sections it returns; pass `include_calls="all"` to see every edge.
|
|
149
|
+
|
|
150
|
+
**Invisible repository cache.** The map is a derived artifact, so it lives in
|
|
151
|
+
`.git/info/tier/repo.tier` inside a worktree (which Git ignores) or in a user cache directory
|
|
152
|
+
outside it. `.git` is followed when it is a pointer file rather than a directory, so linked
|
|
153
|
+
worktrees and submodules resolve to their own git directory. A tracked `repo.tier` is still
|
|
154
|
+
honoured when one exists. Nothing is written into your working tree, so `git status` stays clean.
|
|
155
|
+
|
|
156
|
+
**Hybrid search with precomputed IDF.** BM25-style term scoring over split identifiers, a compact
|
|
157
|
+
synonym map, character 3-gram fallback for typos, and navigation priors. Document frequencies are
|
|
158
|
+
computed once per query, which keeps a search over 800 sections near 200 ms with no index to build.
|
|
159
|
+
|
|
160
|
+
## Performance
|
|
161
|
+
|
|
162
|
+
Measured with the `o200k_base` tokenizer. These are token counts and retrieval hit rates, not LLM
|
|
163
|
+
answer accuracy. Methodology and reproduction steps are in
|
|
164
|
+
[`benchmarks/README.md`](benchmarks/README.md).
|
|
165
|
+
|
|
166
|
+
| Workload | Measure | Result |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| Repository overview | raw source → full map → L0 → L0 + one module | 72,859 → 14,375 → 828 → 1,784 tokens (−98%) |
|
|
169
|
+
| End-to-end 4-layer workflow | index, find, L2, L3 slices, impact + sync | 5,003 tokens (−93.1%), 3/3 exact slices |
|
|
170
|
+
| 36-file polyglot repository | surgical L0 + target L2 + 7 slices | 3,008 vs 8,249 tokens (−64%), 116/116 symbols |
|
|
171
|
+
| 197-section blind retrieval | 15 queries | 15/15 hit@1, mean top-1 fetch 134 tokens |
|
|
172
|
+
|
|
173
|
+
[`benchmarks/BENCH.txt`](benchmarks/BENCH.txt) carries the full Track A–I breakdown, including the
|
|
174
|
+
record-encoding and nested-config tracks, the per-query retrieval logs, and the raw output behind
|
|
175
|
+
every row above.
|
|
176
|
+
|
|
177
|
+
## Supported clients
|
|
178
|
+
|
|
179
|
+
`tier install` configures 25 agent clients across JSON, TOML, YAML, and `SKILL.md` schemas. It
|
|
180
|
+
backs up every file it touches, merges instead of overwriting, supports `--dry-run` and
|
|
181
|
+
`--uninstall`, and validates the result with `tier doctor`.
|
|
182
|
+
|
|
183
|
+
<details>
|
|
184
|
+
<summary><b>Client list and config paths</b></summary>
|
|
185
|
+
|
|
186
|
+
| Client | Scope | Config |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| Claude Code | project / user | `.mcp.json` + `.claude/settings.json`, `~/.claude.json` |
|
|
189
|
+
| Claude Desktop | user | `claude_desktop_config.json` |
|
|
190
|
+
| Command Code | project / user | `.commandcode/mcp.json`, `~/.commandcode/mcp.json` |
|
|
191
|
+
| OpenCode | project / global | `opencode.json`, `~/.config/opencode/opencode.json` |
|
|
192
|
+
| OpenAI Codex CLI | project / user | `.codex/config.toml`, `~/.codex/config.toml` |
|
|
193
|
+
| Gemini CLI | project / user | `.gemini/settings.json` + `GEMINI.md` |
|
|
194
|
+
| Cursor | project / global | `.cursor/mcp.json`, `~/.cursor/mcp.json` |
|
|
195
|
+
| VS Code Copilot | project | `.vscode/mcp.json` |
|
|
196
|
+
| Windsurf | user | `~/.codeium/windsurf/mcp_config.json` |
|
|
197
|
+
| Roo Code / Cline | project | `.roo/mcp.json` |
|
|
198
|
+
| Trae | project | `.trae/mcp.json` |
|
|
199
|
+
| JetBrains Junie | project / user | `.junie/mcp/mcp.json`, `~/.junie/mcp/mcp.json` |
|
|
200
|
+
| Kiro | project | `.kiro/settings/mcp.json` |
|
|
201
|
+
| Warp | user | `~/.warp/.mcp.json` |
|
|
202
|
+
| Zed | user | `~/.config/zed/settings.json` (`context_servers`) |
|
|
203
|
+
| Continue.dev | project | `.continue/mcpServers/tier.yaml` |
|
|
204
|
+
| Agent Skills | project / user | `.agents/skills/tier/SKILL.md` |
|
|
205
|
+
|
|
206
|
+
Any other client can be configured with `--custom-config PATH --schema NAME`, and
|
|
207
|
+
`tier config-snippet` prints ready-to-copy JSON, TOML, YAML, and `SKILL.md` snippets plus native
|
|
208
|
+
CLI one-liners.
|
|
209
|
+
|
|
210
|
+
</details>
|
|
211
|
+
|
|
212
|
+
<details>
|
|
213
|
+
<summary><b>Optional extras</b></summary>
|
|
214
|
+
|
|
215
|
+
```sh
|
|
216
|
+
uv tool install "tier-ctx[ui] @ git+https://github.com/000jd/tier" # rich terminal UI
|
|
217
|
+
uv tool install "tier-ctx[mcp] @ git+https://github.com/000jd/tier" # official MCP SDK
|
|
218
|
+
uv tool install "tier-ctx[treesitter] @ git+https://github.com/000jd/tier" # multi-language AST
|
|
219
|
+
uv tool install "tier-ctx[all] @ git+https://github.com/000jd/tier"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- `[ui]` adds `rich` rendering for `tier install` and `tier doctor`.
|
|
223
|
+
- `[mcp]` adds the official SDK adapter (`tier-mcp-sdk`), Streamable HTTP, and MCP Inspector.
|
|
224
|
+
- `[treesitter]` adds Tree-sitter grammars for TypeScript, JavaScript, Go, Rust, C, C++, Java,
|
|
225
|
+
and Ruby. Without it the stdlib extractors are used, and repository maps are byte-identical.
|
|
226
|
+
|
|
227
|
+
</details>
|
|
228
|
+
|
|
229
|
+
## Documentation
|
|
230
|
+
|
|
231
|
+
- [`docs/spec.md`](docs/spec.md) — the format, parser rules, and retrieval protocol
|
|
232
|
+
- [`ARCHITECTURE.md`](ARCHITECTURE.md) — module boundaries, layer flow, and invariants
|
|
233
|
+
- [`SKILL.md`](SKILL.md) — the canonical Agent Skill: entry point, CLI and MCP tables, working rules
|
|
234
|
+
- [`docs/cli.md`](docs/cli.md) — every command and flag
|
|
235
|
+
- [`docs/mcp.md`](docs/mcp.md) — the 12 MCP tools, one-turn workflow, and cache behaviour
|
|
236
|
+
- [`benchmarks/README.md`](benchmarks/README.md) — workload definitions and methodology
|
|
237
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — development setup and extension guides
|
|
238
|
+
- [`CHANGELOG.md`](CHANGELOG.md) — release history
|
|
239
|
+
|
|
240
|
+
## Limits
|
|
241
|
+
|
|
242
|
+
Only token counts, retrieval identifiers, and exact-slice coverage are measured — not LLM answer
|
|
243
|
+
accuracy. Claude's tokenizer is not public, so counts use OpenAI `o200k` via a persistent Node
|
|
244
|
+
worker when available, and a characters/4 estimate otherwise. `tier hook` treats its token budget
|
|
245
|
+
as a contract: it skips a payload only when it is provably within budget, otherwise it counts
|
|
246
|
+
exactly and verifies the output. `tier sync` trusts recorded mtime and size as a cache; use
|
|
247
|
+
`tier sync --rehash` after tooling that restores timestamps. JS/TS/Go/Rust symbol extraction is
|
|
248
|
+
heuristic without the `[treesitter]` extra.
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT © 2026 000jd. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tier-ctx"
|
|
7
|
+
version = "1.2.1"
|
|
8
|
+
description = "TIER: layered context, repository graphs, code slicing, and agent memory"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [{ name = "000jd" }]
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
dependencies = []
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
ui = ["rich>=13.0"]
|
|
17
|
+
mcp = ["mcp>=2.0"]
|
|
18
|
+
treesitter = ["tree-sitter-language-pack>=0.7.0"]
|
|
19
|
+
all = ["rich>=13.0", "mcp>=2.0", "tree-sitter-language-pack>=0.7.0"]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
tier = "tier.cli:main"
|
|
23
|
+
tier-mcp = "tier.mcp.server:main"
|
|
24
|
+
tier-mcp-sdk = "tier.mcp.sdk_server:main"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools]
|
|
27
|
+
package-dir = {"" = "src"}
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["src"]
|
|
31
|
+
include = ["tier*"]
|
tier_ctx-1.2.1/setup.cfg
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""TIER: layered context, repository graphs, code slicing, and agent memory."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .models import (
|
|
5
|
+
VERSION, Doc, Section, SIGIL_CHARS, SEC_RE, HDR_RE, TABLE_RE, DATA_OPEN_RE,
|
|
6
|
+
RULE_START, STOP, SHELL_LANGS, TABLE_MODES, _W, _atomic_write, _clip, _inside,
|
|
7
|
+
_read_text, _table_count,
|
|
8
|
+
)
|
|
9
|
+
from .tokens import count_tokens
|
|
10
|
+
from .parser import (
|
|
11
|
+
_csv_cells, _data_closes, _data_open, _kv_split, _live_lines, _load, _parse_kv,
|
|
12
|
+
_render_table, _repo_file_meta, _table_rows, dumps, is_repo_doc, nodes, parse,
|
|
13
|
+
render_lines, render_section, to_obj, wrap_untrusted,
|
|
14
|
+
)
|
|
15
|
+
from .lint import lint
|
|
16
|
+
from .converter import (
|
|
17
|
+
_auto_abstract, _clip as _converter_clip, _csv_row, _esc, _json_body, _json_cell,
|
|
18
|
+
_json_kv_lines, _json_scalar, _make_id, _md_cells, _one_line_text, _records_table,
|
|
19
|
+
_ref_topics, _table_name, from_json, from_md, from_payload, to_md,
|
|
20
|
+
)
|
|
21
|
+
from .retrieval import (
|
|
22
|
+
QSTOP, SYNONYMS, Session, _find_legacy, _index_tokens, _is_navigation_section,
|
|
23
|
+
_is_test_module, _norm, _query_terms, _rule_count, _synonym_map, _trigram_sim,
|
|
24
|
+
_trigrams, find, get, index, resolve, split_identifier,
|
|
25
|
+
)
|
|
26
|
+
from .repo import (
|
|
27
|
+
JS_CONTROL, LANG_BY_EXT, REPO_ENTRY_NAMES, REPO_SKIP_DIRS, REPO_SOURCE_EXTS,
|
|
28
|
+
_brace_end, _build_graph, _changed_symbols, _extract_symbols, _git_changed_paths,
|
|
29
|
+
_go_module, _go_symbols, _js_arrow_name, _js_expr_end, _js_rhs_arrow, _js_symbols,
|
|
30
|
+
_leading_comment, _py_module_name, _py_symbols, _reachable, _repo_add_deps, _repo_id,
|
|
31
|
+
_repo_info, _repo_section, _repo_summary, _resolve_go_imports, _resolve_impact_target,
|
|
32
|
+
_resolve_js_imports, _resolve_module, _resolve_py_imports, _resolve_rust_mods,
|
|
33
|
+
_rust_symbols, _sha256, _sig_line, _slice_range, _strip_code_strings, _symbol_rows,
|
|
34
|
+
_symbol_table, _top_symbol_names, from_repo, impact, render_impact, repo_source_files,
|
|
35
|
+
slice, slice_symbols, sync_repo,
|
|
36
|
+
)
|
|
37
|
+
from .memory import _validate_section_body, remember
|
|
38
|
+
from .pipe import (
|
|
39
|
+
LOG_CMD_RE, LOG_ERR_RE, LOG_WARN_RE, _fit_text, _log_shape, _text_fields, _tokens,
|
|
40
|
+
detect_stream_format, from_stream, hook_process, log_doc, pipe_compress,
|
|
41
|
+
)
|
|
42
|
+
from .plugin import (
|
|
43
|
+
TIER_BEGIN, TIER_END, _hook_commands, _looks_like_tier_hook, _merge_hook_settings,
|
|
44
|
+
_plugin_instructions, _write_marked_instructions, init_plugin,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
__version__ = VERSION
|
|
48
|
+
__all__ = [
|
|
49
|
+
"VERSION", "Doc", "Section", "parse", "dumps", "index", "find", "get", "slice",
|
|
50
|
+
"slice_symbols", "impact", "sync_repo", "from_repo", "remember", "Session",
|
|
51
|
+
"wrap_untrusted", "from_md", "from_json", "from_payload", "to_md", "lint", "nodes",
|
|
52
|
+
"count_tokens", "pipe_compress", "hook_process", "init_plugin",
|
|
53
|
+
]
|