rm-contextos 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.
- rm_contextos-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- rm_contextos-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- rm_contextos-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- rm_contextos-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- rm_contextos-0.1.0/.github/workflows/ci.yml +42 -0
- rm_contextos-0.1.0/.github/workflows/publish.yml +45 -0
- rm_contextos-0.1.0/.gitignore +15 -0
- rm_contextos-0.1.0/ARCHITECTURE.md +257 -0
- rm_contextos-0.1.0/AUDIT_REPORT.md +200 -0
- rm_contextos-0.1.0/CHANGELOG.md +33 -0
- rm_contextos-0.1.0/CLAUDE.md +105 -0
- rm_contextos-0.1.0/CODEX.md +175 -0
- rm_contextos-0.1.0/CODE_OF_CONDUCT.md +32 -0
- rm_contextos-0.1.0/CONTRIBUTING.md +51 -0
- rm_contextos-0.1.0/LICENSE +183 -0
- rm_contextos-0.1.0/PKG-INFO +683 -0
- rm_contextos-0.1.0/README.md +452 -0
- rm_contextos-0.1.0/RISK_REGISTER.md +189 -0
- rm_contextos-0.1.0/ROADMAP.md +77 -0
- rm_contextos-0.1.0/SECURITY.md +37 -0
- rm_contextos-0.1.0/TASKS.md +123 -0
- rm_contextos-0.1.0/TEST_PLAN.md +269 -0
- rm_contextos-0.1.0/contextos/__init__.py +6 -0
- rm_contextos-0.1.0/contextos/cli/__init__.py +3 -0
- rm_contextos-0.1.0/contextos/cli/commands/__init__.py +3 -0
- rm_contextos-0.1.0/contextos/cli/commands/export.py +270 -0
- rm_contextos-0.1.0/contextos/cli/commands/init.py +96 -0
- rm_contextos-0.1.0/contextos/cli/commands/memory.py +262 -0
- rm_contextos-0.1.0/contextos/cli/commands/pack.py +173 -0
- rm_contextos-0.1.0/contextos/cli/commands/scan.py +98 -0
- rm_contextos-0.1.0/contextos/cli/commands/task.py +153 -0
- rm_contextos-0.1.0/contextos/cli/main.py +49 -0
- rm_contextos-0.1.0/contextos/core/__init__.py +3 -0
- rm_contextos-0.1.0/contextos/core/ast_extractor.py +234 -0
- rm_contextos-0.1.0/contextos/core/compression.py +74 -0
- rm_contextos-0.1.0/contextos/core/context_selector.py +641 -0
- rm_contextos-0.1.0/contextos/core/dependency_graph.py +339 -0
- rm_contextos-0.1.0/contextos/core/headroom_adapter.py +116 -0
- rm_contextos-0.1.0/contextos/core/initializer.py +285 -0
- rm_contextos-0.1.0/contextos/core/pack_builder.py +372 -0
- rm_contextos-0.1.0/contextos/core/repo_index.py +528 -0
- rm_contextos-0.1.0/contextos/core/safety.py +174 -0
- rm_contextos-0.1.0/contextos/core/scanner.py +178 -0
- rm_contextos-0.1.0/contextos/core/secret_detector.py +253 -0
- rm_contextos-0.1.0/contextos/core/summarizer.py +587 -0
- rm_contextos-0.1.0/contextos/core/token_counter.py +52 -0
- rm_contextos-0.1.0/contextos/exporters/__init__.py +1 -0
- rm_contextos-0.1.0/contextos/exporters/aider.py +62 -0
- rm_contextos-0.1.0/contextos/exporters/base.py +206 -0
- rm_contextos-0.1.0/contextos/exporters/claude.py +64 -0
- rm_contextos-0.1.0/contextos/exporters/codex.py +62 -0
- rm_contextos-0.1.0/contextos/exporters/cursor.py +62 -0
- rm_contextos-0.1.0/demo/demo.gif +0 -0
- rm_contextos-0.1.0/demo.tape +45 -0
- rm_contextos-0.1.0/docs/ARCHITECTURE.md +232 -0
- rm_contextos-0.1.0/docs/CONTRIBUTING.md +172 -0
- rm_contextos-0.1.0/docs/HEADROOM.md +128 -0
- rm_contextos-0.1.0/docs/INSTALL.md +111 -0
- rm_contextos-0.1.0/docs/MCP_SETUP.md +180 -0
- rm_contextos-0.1.0/docs/ROADMAP.md +100 -0
- rm_contextos-0.1.0/docs/SAFETY.md +145 -0
- rm_contextos-0.1.0/docs/USAGE.md +259 -0
- rm_contextos-0.1.0/examples/README.md +174 -0
- rm_contextos-0.1.0/examples/monorepo/README.md +38 -0
- rm_contextos-0.1.0/examples/monorepo/package.json +14 -0
- rm_contextos-0.1.0/examples/monorepo/packages/api/package.json +17 -0
- rm_contextos-0.1.0/examples/monorepo/packages/api/src/auth.ts +48 -0
- rm_contextos-0.1.0/examples/monorepo/packages/api/src/index.ts +15 -0
- rm_contextos-0.1.0/examples/monorepo/packages/api/src/routes.ts +40 -0
- rm_contextos-0.1.0/examples/monorepo/packages/shared/package.json +8 -0
- rm_contextos-0.1.0/examples/monorepo/packages/shared/src/types.ts +25 -0
- rm_contextos-0.1.0/examples/monorepo/packages/shared/src/validators.ts +30 -0
- rm_contextos-0.1.0/examples/monorepo/packages/web/package.json +19 -0
- rm_contextos-0.1.0/examples/monorepo/packages/web/src/App.tsx +40 -0
- rm_contextos-0.1.0/examples/monorepo/packages/web/src/components/ProjectCard.tsx +25 -0
- rm_contextos-0.1.0/examples/monorepo/turbo.json +8 -0
- rm_contextos-0.1.0/examples/python_fastapi/README.md +34 -0
- rm_contextos-0.1.0/examples/python_fastapi/app/__init__.py +0 -0
- rm_contextos-0.1.0/examples/python_fastapi/app/auth.py +79 -0
- rm_contextos-0.1.0/examples/python_fastapi/app/database.py +23 -0
- rm_contextos-0.1.0/examples/python_fastapi/app/main.py +24 -0
- rm_contextos-0.1.0/examples/python_fastapi/app/models.py +45 -0
- rm_contextos-0.1.0/examples/python_fastapi/app/routes/__init__.py +0 -0
- rm_contextos-0.1.0/examples/python_fastapi/app/routes/items.py +48 -0
- rm_contextos-0.1.0/examples/python_fastapi/app/routes/users.py +52 -0
- rm_contextos-0.1.0/examples/python_fastapi/requirements.txt +8 -0
- rm_contextos-0.1.0/examples/python_fastapi/tests/conftest.py +12 -0
- rm_contextos-0.1.0/examples/python_fastapi/tests/test_auth.py +29 -0
- rm_contextos-0.1.0/examples/python_fastapi/tests/test_users.py +45 -0
- rm_contextos-0.1.0/examples/react_typescript/README.md +33 -0
- rm_contextos-0.1.0/examples/react_typescript/package.json +25 -0
- rm_contextos-0.1.0/examples/react_typescript/src/App.tsx +18 -0
- rm_contextos-0.1.0/examples/react_typescript/src/api/client.ts +41 -0
- rm_contextos-0.1.0/examples/react_typescript/src/components/Auth/LoginForm.test.tsx +31 -0
- rm_contextos-0.1.0/examples/react_typescript/src/components/Auth/LoginForm.tsx +49 -0
- rm_contextos-0.1.0/examples/react_typescript/src/components/Dashboard/Dashboard.tsx +58 -0
- rm_contextos-0.1.0/examples/react_typescript/src/hooks/useApi.ts +47 -0
- rm_contextos-0.1.0/examples/react_typescript/src/hooks/useAuth.ts +53 -0
- rm_contextos-0.1.0/examples/react_typescript/src/types/index.ts +23 -0
- rm_contextos-0.1.0/examples/react_typescript/tsconfig.json +15 -0
- rm_contextos-0.1.0/pyproject.toml +98 -0
- rm_contextos-0.1.0/tests/__init__.py +0 -0
- rm_contextos-0.1.0/tests/cli/__init__.py +0 -0
- rm_contextos-0.1.0/tests/cli/test_commands.py +153 -0
- rm_contextos-0.1.0/tests/cli/test_imports.py +46 -0
- rm_contextos-0.1.0/tests/cli/test_init.py +221 -0
- rm_contextos-0.1.0/tests/cli/test_memory.py +552 -0
- rm_contextos-0.1.0/tests/cli/test_pack.py +590 -0
- rm_contextos-0.1.0/tests/cli/test_task.py +338 -0
- rm_contextos-0.1.0/tests/conftest.py +29 -0
- rm_contextos-0.1.0/tests/core/__init__.py +0 -0
- rm_contextos-0.1.0/tests/core/test_ast_extractor.py +215 -0
- rm_contextos-0.1.0/tests/core/test_compression.py +422 -0
- rm_contextos-0.1.0/tests/core/test_context_selector.py +802 -0
- rm_contextos-0.1.0/tests/core/test_dependency_graph.py +598 -0
- rm_contextos-0.1.0/tests/core/test_repo_index.py +635 -0
- rm_contextos-0.1.0/tests/core/test_scanner.py +476 -0
- rm_contextos-0.1.0/tests/core/test_secret_detector.py +749 -0
- rm_contextos-0.1.0/tests/core/test_summarizer.py +770 -0
- rm_contextos-0.1.0/tests/core/test_token_counter.py +265 -0
- rm_contextos-0.1.0/tests/examples/__init__.py +0 -0
- rm_contextos-0.1.0/tests/examples/test_examples.py +454 -0
- rm_contextos-0.1.0/tests/exporters/__init__.py +0 -0
- rm_contextos-0.1.0/tests/exporters/test_exporters.py +765 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Report a reproducible problem in ContextOS
|
|
4
|
+
title: "[Bug]: "
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ""
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What happened?
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Steps to reproduce
|
|
13
|
+
|
|
14
|
+
1.
|
|
15
|
+
2.
|
|
16
|
+
3.
|
|
17
|
+
|
|
18
|
+
## Expected behavior
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Environment
|
|
22
|
+
|
|
23
|
+
- ContextOS version:
|
|
24
|
+
- Python version:
|
|
25
|
+
- OS:
|
|
26
|
+
- Install method:
|
|
27
|
+
|
|
28
|
+
## Logs or output
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
|
|
32
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an improvement for ContextOS
|
|
4
|
+
title: "[Feature]: "
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ""
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Problem
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Proposed solution
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Alternatives considered
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Additional context
|
|
19
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
-
|
|
4
|
+
|
|
5
|
+
## Validation
|
|
6
|
+
|
|
7
|
+
- [ ] `pytest`
|
|
8
|
+
- [ ] `ruff check .`
|
|
9
|
+
- [ ] `mypy .`
|
|
10
|
+
- [ ] `ruff format --check contextos/ tests/`
|
|
11
|
+
|
|
12
|
+
## Safety Checklist
|
|
13
|
+
|
|
14
|
+
- [ ] No unrelated behavior changes.
|
|
15
|
+
- [ ] Secret handling remains safe by default.
|
|
16
|
+
- [ ] Filesystem reads/writes are scoped and intentional.
|
|
17
|
+
- [ ] Documentation or changelog updated when user-visible behavior changes.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test (Python ${{ matrix.python-version }}, ${{ matrix.os }})
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
17
|
+
os: [ubuntu-latest, macos-latest]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade pip
|
|
30
|
+
pip install -e ".[dev]"
|
|
31
|
+
|
|
32
|
+
- name: Lint (ruff)
|
|
33
|
+
run: ruff check contextos/ tests/
|
|
34
|
+
|
|
35
|
+
- name: Format check (ruff)
|
|
36
|
+
run: ruff format --check contextos/ tests/
|
|
37
|
+
|
|
38
|
+
- name: Type check (mypy)
|
|
39
|
+
run: mypy contextos/
|
|
40
|
+
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: pytest --cov=contextos --cov-report=term-missing --cov-fail-under=80
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Build distribution
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Set up Python
|
|
15
|
+
uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Install build
|
|
20
|
+
run: pip install build
|
|
21
|
+
|
|
22
|
+
- name: Build package
|
|
23
|
+
run: python -m build
|
|
24
|
+
|
|
25
|
+
- name: Upload dist artifacts
|
|
26
|
+
uses: actions/upload-artifact@v4
|
|
27
|
+
with:
|
|
28
|
+
name: dist
|
|
29
|
+
path: dist/
|
|
30
|
+
|
|
31
|
+
publish:
|
|
32
|
+
name: Publish to PyPI
|
|
33
|
+
needs: build
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- name: Download dist artifacts
|
|
37
|
+
uses: actions/download-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: dist
|
|
40
|
+
path: dist/
|
|
41
|
+
|
|
42
|
+
- name: Publish to PyPI
|
|
43
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
44
|
+
with:
|
|
45
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# ContextOS Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
ContextOS is a pipeline: a repository enters one end, a context pack exits the other. Each stage is a pure transformation — no side effects, no network, no mutation of the source repo.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
repo path
|
|
9
|
+
│
|
|
10
|
+
▼
|
|
11
|
+
┌──────────┐
|
|
12
|
+
│ Scanner │ walks files, respects .gitignore, classifies by language
|
|
13
|
+
└──────────┘
|
|
14
|
+
│ FileIndex
|
|
15
|
+
▼
|
|
16
|
+
┌─────────────┐
|
|
17
|
+
│ Intelligence│ extracts symbols, imports, call edges → RepoGraph
|
|
18
|
+
└─────────────┘
|
|
19
|
+
│ RepoGraph
|
|
20
|
+
▼
|
|
21
|
+
┌──────────┐
|
|
22
|
+
│ Selector │ takes task + budget → ranks and clips chunks
|
|
23
|
+
└──────────┘
|
|
24
|
+
│ ContextBundle
|
|
25
|
+
▼
|
|
26
|
+
┌────────────┐
|
|
27
|
+
│ Compressor │ optional Headroom pass — shrinks tokens further
|
|
28
|
+
└────────────┘
|
|
29
|
+
│ CompressedBundle
|
|
30
|
+
▼
|
|
31
|
+
┌──────────┐
|
|
32
|
+
│ Exporter │ formats for target agent
|
|
33
|
+
└──────────┘
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
context pack (file on disk)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Data Models (`models.py`)
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
@dataclass
|
|
43
|
+
class FileNode:
|
|
44
|
+
path: Path # absolute path
|
|
45
|
+
rel_path: Path # relative to repo root
|
|
46
|
+
language: str # "python", "typescript", etc.
|
|
47
|
+
size_bytes: int
|
|
48
|
+
lines: int
|
|
49
|
+
content: str
|
|
50
|
+
tokens: int # estimated at scan time
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class ChunkNode:
|
|
54
|
+
file: FileNode
|
|
55
|
+
start_line: int
|
|
56
|
+
end_line: int
|
|
57
|
+
chunk_type: str # "function", "class", "module", "block"
|
|
58
|
+
name: str | None # symbol name if extractable
|
|
59
|
+
content: str
|
|
60
|
+
tokens: int
|
|
61
|
+
score: float # set by Selector; 0.0 at creation
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class RepoGraph:
|
|
65
|
+
files: list[FileNode]
|
|
66
|
+
chunks: list[ChunkNode]
|
|
67
|
+
imports: dict[Path, list[Path]] # file → files it imports
|
|
68
|
+
symbols: dict[str, list[ChunkNode]] # symbol name → defining chunks
|
|
69
|
+
call_edges: list[tuple[str, str]] # (caller_symbol, callee_symbol)
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class ContextBundle:
|
|
73
|
+
task: str
|
|
74
|
+
repo_root: Path
|
|
75
|
+
chunks: list[ChunkNode] # selected, ordered
|
|
76
|
+
total_tokens: int
|
|
77
|
+
budget: int
|
|
78
|
+
strategy: str
|
|
79
|
+
created_at: str # ISO 8601, deterministic
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class CompressedBundle:
|
|
83
|
+
bundle: ContextBundle
|
|
84
|
+
compressed_content: str
|
|
85
|
+
original_tokens: int
|
|
86
|
+
compressed_tokens: int
|
|
87
|
+
compression_ratio: float
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Module Breakdown
|
|
91
|
+
|
|
92
|
+
### `scanner/`
|
|
93
|
+
|
|
94
|
+
**`walker.py`** — `RepoWalker`
|
|
95
|
+
- Walks a directory tree using `pathlib.Path.rglob`
|
|
96
|
+
- Reads `.gitignore` via `gitignore-parser` (or built-in simple parser)
|
|
97
|
+
- Skips: binary files, files over a size limit (default 512 KB), lock files, build artifacts
|
|
98
|
+
- Returns an ordered list of `FileNode` (ordered by `rel_path` for determinism)
|
|
99
|
+
|
|
100
|
+
**`classifier.py`** — `LanguageClassifier`
|
|
101
|
+
- Maps file extensions to language names
|
|
102
|
+
- Detects shebangs for extensionless scripts
|
|
103
|
+
- Returns language string or `"unknown"`
|
|
104
|
+
|
|
105
|
+
**`index.py`** — `FileIndex`
|
|
106
|
+
- Thin wrapper over `list[FileNode]`
|
|
107
|
+
- Provides lookup by path, by language, by glob pattern
|
|
108
|
+
- Computes aggregate stats (total files, total tokens, language breakdown)
|
|
109
|
+
|
|
110
|
+
### `intelligence/`
|
|
111
|
+
|
|
112
|
+
**`chunker.py`** — `Chunker`
|
|
113
|
+
- Splits files into `ChunkNode` objects
|
|
114
|
+
- Two modes:
|
|
115
|
+
- **AST mode** (tree-sitter, optional): function/class/method-level chunks
|
|
116
|
+
- **Line mode** (default): fixed-size overlapping windows (e.g. 50 lines, 10-line overlap)
|
|
117
|
+
- Chunk size is bounded: minimum 5 lines, maximum 200 lines
|
|
118
|
+
- Always produces at least one chunk per file (the whole file if small)
|
|
119
|
+
|
|
120
|
+
**`extractor.py`** — `SymbolExtractor`
|
|
121
|
+
- Runs in AST mode only
|
|
122
|
+
- Extracts: function names, class names, import statements, exported symbols
|
|
123
|
+
- Falls back to regex-based extraction for unsupported languages
|
|
124
|
+
|
|
125
|
+
**`graph.py`** — `GraphBuilder`
|
|
126
|
+
- Builds `RepoGraph` from `FileIndex` + extracted symbols
|
|
127
|
+
- Import resolution: converts `from foo.bar import baz` → `Path` edges
|
|
128
|
+
- Does not execute code; static analysis only
|
|
129
|
+
- Cycle detection: records import cycles as metadata, does not fail on them
|
|
130
|
+
|
|
131
|
+
### `selector/`
|
|
132
|
+
|
|
133
|
+
**`ranker.py`** — `Ranker`
|
|
134
|
+
- Assigns a relevance score to each `ChunkNode` given a task string
|
|
135
|
+
- Scoring is additive; no single signal dominates:
|
|
136
|
+
|
|
137
|
+
| Signal | Weight | Description |
|
|
138
|
+
|--------|--------|-------------|
|
|
139
|
+
| Keyword overlap | 0.40 | Task tokens that appear in chunk content |
|
|
140
|
+
| Symbol match | 0.25 | Task tokens matching extracted symbol names |
|
|
141
|
+
| Import centrality | 0.20 | Files imported by many others score higher |
|
|
142
|
+
| Recency proxy | 0.15 | Files modified recently score slightly higher |
|
|
143
|
+
|
|
144
|
+
- Scores are normalized to [0.0, 1.0]
|
|
145
|
+
- Scoring is purely local: no embeddings, no LLM calls
|
|
146
|
+
|
|
147
|
+
**`budget.py`** — `BudgetEnforcer`
|
|
148
|
+
- Takes ranked `ChunkNode` list and a token budget
|
|
149
|
+
- Greedy selection: add highest-scoring chunks until budget is exhausted
|
|
150
|
+
- Two modes:
|
|
151
|
+
- `greedy`: pure score-descending fill
|
|
152
|
+
- `balanced`: ensures at least one chunk from each file that appears in the top N
|
|
153
|
+
|
|
154
|
+
**`strategy.py`** — `SelectionStrategy`
|
|
155
|
+
- Named presets that combine Ranker weights + BudgetEnforcer mode
|
|
156
|
+
- Built-in: `default`, `deep-file` (fewer files, deeper), `wide-repo` (more files, shallower)
|
|
157
|
+
- Custom strategies via TOML config
|
|
158
|
+
|
|
159
|
+
### `compressor/`
|
|
160
|
+
|
|
161
|
+
**`headroom.py`** — `HeadroomCompressor`
|
|
162
|
+
- Optional; only active when `headroom-ai` is installed and proxy is reachable
|
|
163
|
+
- Sends `ContextBundle` content to local Headroom proxy at `http://127.0.0.1:8787`
|
|
164
|
+
- Returns `CompressedBundle` with compressed text and token delta
|
|
165
|
+
- Fails gracefully: if proxy is unreachable, returns original bundle unchanged
|
|
166
|
+
- No data leaves the machine; proxy is local
|
|
167
|
+
|
|
168
|
+
### `exporter/`
|
|
169
|
+
|
|
170
|
+
Each exporter takes a `ContextBundle | CompressedBundle` and returns a `str`.
|
|
171
|
+
|
|
172
|
+
**`base.py`** — `BaseExporter` (abstract)
|
|
173
|
+
- `render(bundle) -> str`
|
|
174
|
+
- `filename() -> str`
|
|
175
|
+
|
|
176
|
+
**`markdown.py`** — renders as fenced code blocks with file headers
|
|
177
|
+
|
|
178
|
+
**`xml.py`** — renders as `<file path="...">` XML tags (Claude-style)
|
|
179
|
+
|
|
180
|
+
**`claude.py`** — XML blocks + a task preamble formatted for Claude Code's system prompt
|
|
181
|
+
|
|
182
|
+
**`codex.py`** — plain text blocks formatted for `AGENTS.md` injection
|
|
183
|
+
|
|
184
|
+
**`cursor.py`** — splits into `.cursorrules` (repo summary) + `context.md` (file blocks)
|
|
185
|
+
|
|
186
|
+
**`aider.py`** — tab-separated file list + content blocks for Aider's `--read` input
|
|
187
|
+
|
|
188
|
+
**`json_.py`** — machine-readable JSON: metadata + chunks as structured objects
|
|
189
|
+
|
|
190
|
+
### `cli.py`
|
|
191
|
+
|
|
192
|
+
Built with Typer. Three top-level commands:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
contextos scan <repo> # index and summarize
|
|
196
|
+
contextos pack <repo> --task --budget --format # select and export
|
|
197
|
+
contextos graph <repo> # print import graph (optional)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
All commands are read-only on the source repo. Output goes to stdout or `--out <path>`.
|
|
201
|
+
|
|
202
|
+
## Token Counting
|
|
203
|
+
|
|
204
|
+
Token counting uses a priority chain:
|
|
205
|
+
|
|
206
|
+
1. `tiktoken` with the model's actual tokenizer (if installed and model known)
|
|
207
|
+
2. `tiktoken` with `cl100k_base` as a universal approximation
|
|
208
|
+
3. `len(content) // 4` as a last-resort fallback
|
|
209
|
+
|
|
210
|
+
The counting method is reported in bundle metadata so consumers know which approximation was used.
|
|
211
|
+
|
|
212
|
+
## Determinism Contract
|
|
213
|
+
|
|
214
|
+
Given identical inputs (repo state, task string, budget, strategy), ContextOS always produces byte-for-byte identical output. This is guaranteed by:
|
|
215
|
+
|
|
216
|
+
- File iteration order: `sorted(path.rglob(...))` — lexicographic, OS-independent
|
|
217
|
+
- Chunk ordering: deterministic by `(file.rel_path, start_line)`
|
|
218
|
+
- Score tie-breaking: ties broken by `(rel_path, start_line)` not by dict insertion order
|
|
219
|
+
- Timestamps: `created_at` in output can be suppressed with `--no-timestamp` for reproducible diffs
|
|
220
|
+
|
|
221
|
+
## Extension Points
|
|
222
|
+
|
|
223
|
+
| Extension | Mechanism |
|
|
224
|
+
|-----------|-----------|
|
|
225
|
+
| New language chunker | Register in `chunker.py` dispatch table |
|
|
226
|
+
| New ranking signal | Add signal function to `ranker.py`, assign weight |
|
|
227
|
+
| New exporter | Subclass `BaseExporter`, register in `cli.py` |
|
|
228
|
+
| Custom selection strategy | TOML config file, loaded by `strategy.py` |
|
|
229
|
+
| Alternative compressor | Implement `Compressor` protocol in `compressor/` |
|
|
230
|
+
|
|
231
|
+
## Security Model
|
|
232
|
+
|
|
233
|
+
- **Read-only**: ContextOS never writes to the source repo
|
|
234
|
+
- **No execution**: no `eval`, no subprocess of repo code, no dynamic imports from repo
|
|
235
|
+
- **No network by default**: all network features gated behind explicit flags
|
|
236
|
+
- **Path traversal**: all paths validated to stay within the declared repo root
|
|
237
|
+
- **Output only to declared destinations**: `--out` flag or stdout; no implicit file writes
|
|
238
|
+
|
|
239
|
+
## Dependencies
|
|
240
|
+
|
|
241
|
+
### Required
|
|
242
|
+
- `typer` — CLI
|
|
243
|
+
- `pathlib` (stdlib) — path handling
|
|
244
|
+
- `dataclasses` (stdlib) — models
|
|
245
|
+
- `tomllib` (stdlib, Python 3.11+) — config parsing
|
|
246
|
+
|
|
247
|
+
### Optional
|
|
248
|
+
- `tiktoken` — accurate token counting
|
|
249
|
+
- `tree-sitter` + language grammars — AST chunking
|
|
250
|
+
- `gitignore-parser` — full `.gitignore` spec support
|
|
251
|
+
- `headroom-ai` — compression
|
|
252
|
+
|
|
253
|
+
### Dev only
|
|
254
|
+
- `pytest` — tests
|
|
255
|
+
- `pytest-cov` — coverage
|
|
256
|
+
- `ruff` — lint + format
|
|
257
|
+
- `mypy` — type checking
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# ContextOS Maintainer Audit Report
|
|
2
|
+
|
|
3
|
+
Date: 2026-06-29
|
|
4
|
+
|
|
5
|
+
Scope: strict open-source maintainer review of the Python package, Typer CLI, scanner,
|
|
6
|
+
context pack builder, secret redaction, tests, packaging metadata, and README.
|
|
7
|
+
|
|
8
|
+
## Executive Summary
|
|
9
|
+
|
|
10
|
+
ContextOS is small, well-tested, and generally conservative about filesystem access. The
|
|
11
|
+
main high-priority issue found during this audit was that the repository advertises a
|
|
12
|
+
strict mypy configuration, but `mypy .` did not pass. That is a release/CI quality gate
|
|
13
|
+
failure, especially for a typed CLI package with dynamic exporter and compression
|
|
14
|
+
boundaries.
|
|
15
|
+
|
|
16
|
+
I fixed only that high-priority issue. Lower-priority documentation and packaging gaps
|
|
17
|
+
are recorded below but intentionally left unfixed.
|
|
18
|
+
|
|
19
|
+
## High-Priority Findings Fixed
|
|
20
|
+
|
|
21
|
+
### HP-1: Configured mypy check failed
|
|
22
|
+
|
|
23
|
+
Severity: High
|
|
24
|
+
|
|
25
|
+
Area: typing, CI/release confidence, dynamic plugin boundaries
|
|
26
|
+
|
|
27
|
+
Evidence:
|
|
28
|
+
|
|
29
|
+
- `pyproject.toml` enables `[tool.mypy] strict = true`.
|
|
30
|
+
- Baseline `mypy .` failed with 27 errors across source and tests.
|
|
31
|
+
- Source-level failures included untyped dynamic provider/exporter calls:
|
|
32
|
+
- `contextos/core/compression.py`
|
|
33
|
+
- `contextos/core/headroom_adapter.py`
|
|
34
|
+
- `contextos/cli/commands/export.py`
|
|
35
|
+
- `contextos/core/secret_detector.py`
|
|
36
|
+
|
|
37
|
+
Impact:
|
|
38
|
+
|
|
39
|
+
- Contributors cannot run the configured quality gate successfully.
|
|
40
|
+
- Dynamic compression/exporter boundaries were opaque to mypy, which weakens confidence
|
|
41
|
+
in CLI code paths that are meant to be extensible.
|
|
42
|
+
|
|
43
|
+
Fix:
|
|
44
|
+
|
|
45
|
+
- Added narrow protocols/casts around dynamic exporter and Headroom imports.
|
|
46
|
+
- Removed a dead helper in secret redaction.
|
|
47
|
+
- Tightened test fixture/type annotations and corrected test imports that referenced a
|
|
48
|
+
helper through the wrong module.
|
|
49
|
+
|
|
50
|
+
Status: Fixed. `mypy .` now passes.
|
|
51
|
+
|
|
52
|
+
## Other Findings Not Fixed In This Pass
|
|
53
|
+
|
|
54
|
+
### README project structure is stale
|
|
55
|
+
|
|
56
|
+
Severity: Medium
|
|
57
|
+
|
|
58
|
+
Area: README clarity
|
|
59
|
+
|
|
60
|
+
Details:
|
|
61
|
+
|
|
62
|
+
The README's `Project Structure` section lists an older layout such as `cli.py`,
|
|
63
|
+
`scanner/`, `intelligence/`, `selector/`, `compressor/`, and `models.py`. The current
|
|
64
|
+
repository uses `contextos/cli/commands/`, `contextos/core/`, and `contextos/exporters/`.
|
|
65
|
+
|
|
66
|
+
Risk:
|
|
67
|
+
|
|
68
|
+
New contributors will look for files that do not exist.
|
|
69
|
+
|
|
70
|
+
Status: Not fixed. Documentation-only and not high priority for this pass.
|
|
71
|
+
|
|
72
|
+
### MIT license declared but no LICENSE file present
|
|
73
|
+
|
|
74
|
+
Severity: Medium
|
|
75
|
+
|
|
76
|
+
Area: packaging, open-source hygiene
|
|
77
|
+
|
|
78
|
+
Details:
|
|
79
|
+
|
|
80
|
+
`pyproject.toml` declares `license = { text = "MIT" }`, but the repository does not
|
|
81
|
+
include a `LICENSE` file.
|
|
82
|
+
|
|
83
|
+
Risk:
|
|
84
|
+
|
|
85
|
+
Package metadata is installable, but GitHub/open-source consumers do not get the full
|
|
86
|
+
license text in the repository.
|
|
87
|
+
|
|
88
|
+
Status: Not fixed. Adding license text should be done by the project owner with the
|
|
89
|
+
correct copyright holder.
|
|
90
|
+
|
|
91
|
+
### Package metadata is minimal
|
|
92
|
+
|
|
93
|
+
Severity: Low
|
|
94
|
+
|
|
95
|
+
Area: packaging
|
|
96
|
+
|
|
97
|
+
Details:
|
|
98
|
+
|
|
99
|
+
The package has name, version, description, readme, Python requirement, dependencies,
|
|
100
|
+
and console script configured. It does not include classifiers, project URLs, authors,
|
|
101
|
+
or keywords.
|
|
102
|
+
|
|
103
|
+
Risk:
|
|
104
|
+
|
|
105
|
+
Lower discoverability and weaker PyPI presentation, but no runtime breakage.
|
|
106
|
+
|
|
107
|
+
Status: Not fixed. Not high priority.
|
|
108
|
+
|
|
109
|
+
## Hardening Follow-Up
|
|
110
|
+
|
|
111
|
+
Additional real-user hardening completed after the initial audit:
|
|
112
|
+
|
|
113
|
+
- `.contextos/` is now always excluded from scans so generated packs and indexes do not
|
|
114
|
+
feed back into later scans or packs.
|
|
115
|
+
- `contextos scan --max-files` now enforces the documented indexing limit instead of
|
|
116
|
+
only changing the displayed count.
|
|
117
|
+
- Scanner relative paths are normalized to POSIX separators for stable output across
|
|
118
|
+
Windows, macOS, and Linux.
|
|
119
|
+
- Generated/noisy notebook and minified asset files are skipped before content indexing.
|
|
120
|
+
- `contextos pack` now regenerates scan data when `.contextos/file_summaries.json` or
|
|
121
|
+
`.contextos/dependency_graph.json` is corrupted or missing.
|
|
122
|
+
- Test-file detection now handles Windows-style separators.
|
|
123
|
+
|
|
124
|
+
Regression coverage added for empty repos, repos without README files, monorepo-shaped
|
|
125
|
+
trees, binary files, large lockfiles, minified JS, notebooks, symlinks, permission errors,
|
|
126
|
+
invalid Unicode, corrupted `.contextos`, repeated init/scan/pack workflows, Windows path
|
|
127
|
+
separators, nested git metadata, and missing optional dependencies.
|
|
128
|
+
|
|
129
|
+
### `--out` writes exactly the user-provided path
|
|
130
|
+
|
|
131
|
+
Severity: Low
|
|
132
|
+
|
|
133
|
+
Area: file handling
|
|
134
|
+
|
|
135
|
+
Details:
|
|
136
|
+
|
|
137
|
+
`contextos pack --out` and `contextos export ... --out` write to the provided path
|
|
138
|
+
without creating parent directories and without restricting the path to the repository.
|
|
139
|
+
|
|
140
|
+
Risk:
|
|
141
|
+
|
|
142
|
+
This is normal CLI behavior for an explicit output flag, but errors may be less friendly
|
|
143
|
+
when a parent directory is missing. It is not an unsafe implicit write because the user
|
|
144
|
+
must provide `--out`.
|
|
145
|
+
|
|
146
|
+
Status: Not fixed. Not high priority.
|
|
147
|
+
|
|
148
|
+
## Areas Reviewed
|
|
149
|
+
|
|
150
|
+
### CLI behavior
|
|
151
|
+
|
|
152
|
+
- Typer command registration is straightforward.
|
|
153
|
+
- Invalid repo and invalid budget paths return controlled `typer.Exit(code=1)`.
|
|
154
|
+
- Tests cover command help, init, scan, task, memory, pack, export, invalid formats,
|
|
155
|
+
missing repos, and secret-related flags.
|
|
156
|
+
|
|
157
|
+
### Unsafe file handling
|
|
158
|
+
|
|
159
|
+
- Scanner uses `os.walk(..., followlinks=False)`.
|
|
160
|
+
- Symlinks escaping the repo root are rejected.
|
|
161
|
+
- Binary files, oversized files, permission errors, encoding errors, `.git`, virtualenvs,
|
|
162
|
+
build artifacts, and cache directories are skipped.
|
|
163
|
+
- `init --force` overwrites `.contextos` templates, but that behavior is explicit and
|
|
164
|
+
documented.
|
|
165
|
+
|
|
166
|
+
### Secret leakage risks
|
|
167
|
+
|
|
168
|
+
- Secret-looking filenames are excluded from context selection.
|
|
169
|
+
- Raw content is redacted before pack/export output unless `--allow-sensitive` is used.
|
|
170
|
+
- `--allow-sensitive` is visibly marked dangerous.
|
|
171
|
+
- Memory and decision commands reject note text that looks like embedded credentials.
|
|
172
|
+
|
|
173
|
+
### Platform issues
|
|
174
|
+
|
|
175
|
+
- The code primarily uses `pathlib`, UTF-8 text IO, and Typer/Rich.
|
|
176
|
+
- Windows-specific chmod semantics are skipped in tests.
|
|
177
|
+
- No high-priority Mac/Linux/Windows runtime issue was found in the reviewed code.
|
|
178
|
+
|
|
179
|
+
### Abstractions and complexity
|
|
180
|
+
|
|
181
|
+
- The separation between scanner, summarizer, selector, pack builder, exporters, and CLI
|
|
182
|
+
commands is reasonable for the package size.
|
|
183
|
+
- Dynamic exporter/provider loading needed clearer typing; fixed.
|
|
184
|
+
- No large unnecessary abstraction was introduced in this pass.
|
|
185
|
+
|
|
186
|
+
## Validation
|
|
187
|
+
|
|
188
|
+
Commands run after fixes:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
.venv/bin/pytest
|
|
192
|
+
.venv/bin/ruff check .
|
|
193
|
+
.venv/bin/mypy .
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Final status after hardening:
|
|
197
|
+
|
|
198
|
+
- `pytest`: 907 passed, 1 skipped
|
|
199
|
+
- `ruff`: passed
|
|
200
|
+
- `mypy`: passed, 49 source files checked
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to ContextOS are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-06-29
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial ContextOS CLI with `init`, `scan`, `task`, `memory`, `pack`, and `export` commands.
|
|
13
|
+
- Deterministic repository scanning, summarization, dependency graphing, and task-aware context selection.
|
|
14
|
+
- Context pack rendering for Markdown/JSON plus Claude Code, Codex, Cursor, and Aider exporters.
|
|
15
|
+
- Secret filename exclusion and content redaction, with explicit `--allow-sensitive` override.
|
|
16
|
+
- Optional Headroom compression provider integration.
|
|
17
|
+
- Project memory and decision-log helpers under `.contextos/`.
|
|
18
|
+
- CI, release documentation, community health files, and issue/PR templates.
|
|
19
|
+
|
|
20
|
+
### Hardened
|
|
21
|
+
|
|
22
|
+
- Exclude generated `.contextos/` outputs from future scans.
|
|
23
|
+
- Enforce `scan --max-files` in written scan output.
|
|
24
|
+
- Skip notebooks and minified generated assets by default.
|
|
25
|
+
- Recover from corrupted `.contextos` scan data during pack generation.
|
|
26
|
+
- Normalize internal scan paths for cross-platform stability.
|
|
27
|
+
|
|
28
|
+
### Verified
|
|
29
|
+
|
|
30
|
+
- `pytest`: 907 passed, 1 skipped.
|
|
31
|
+
- `ruff check .`: passing.
|
|
32
|
+
- `mypy .`: passing.
|
|
33
|
+
- Local wheel build, install, and installed CLI smoke test.
|