refs-mcp 0.2.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.
- refs_mcp-0.2.0/.gitignore +65 -0
- refs_mcp-0.2.0/PKG-INFO +524 -0
- refs_mcp-0.2.0/README.md +507 -0
- refs_mcp-0.2.0/pyproject.toml +194 -0
- refs_mcp-0.2.0/refs_mcp/__init__.py +16 -0
- refs_mcp-0.2.0/refs_mcp/__main__.py +23 -0
- refs_mcp-0.2.0/refs_mcp/_arch.py +92 -0
- refs_mcp-0.2.0/refs_mcp/_build.py +135 -0
- refs_mcp-0.2.0/refs_mcp/_exec_safety.py +88 -0
- refs_mcp-0.2.0/refs_mcp/_link.py +195 -0
- refs_mcp-0.2.0/refs_mcp/api/__init__.py +39 -0
- refs_mcp-0.2.0/refs_mcp/api/_slug.py +96 -0
- refs_mcp-0.2.0/refs_mcp/api/inventory.py +402 -0
- refs_mcp-0.2.0/refs_mcp/api/observability.py +109 -0
- refs_mcp-0.2.0/refs_mcp/api/repo.py +579 -0
- refs_mcp-0.2.0/refs_mcp/auto_clone.py +247 -0
- refs_mcp-0.2.0/refs_mcp/bench/README.md +93 -0
- refs_mcp-0.2.0/refs_mcp/bench/__init__.py +8 -0
- refs_mcp-0.2.0/refs_mcp/bench/backends/__init__.py +1 -0
- refs_mcp-0.2.0/refs_mcp/bench/backends/baseline.py +255 -0
- refs_mcp-0.2.0/refs_mcp/bench/backends/refs_mcp_backend.py +131 -0
- refs_mcp-0.2.0/refs_mcp/bench/corpus.json +1663 -0
- refs_mcp-0.2.0/refs_mcp/bench/corpus.py +127 -0
- refs_mcp-0.2.0/refs_mcp/bench/measure_perf.py +64 -0
- refs_mcp-0.2.0/refs_mcp/bench/metrics.py +185 -0
- refs_mcp-0.2.0/refs_mcp/bench/models.py +166 -0
- refs_mcp-0.2.0/refs_mcp/bench/run_offline_ir.py +385 -0
- refs_mcp-0.2.0/refs_mcp/bench/tools.py +56 -0
- refs_mcp-0.2.0/refs_mcp/bootstrap.py +290 -0
- refs_mcp-0.2.0/refs_mcp/cli.py +517 -0
- refs_mcp-0.2.0/refs_mcp/config.py +95 -0
- refs_mcp-0.2.0/refs_mcp/contracts/__init__.py +56 -0
- refs_mcp-0.2.0/refs_mcp/contracts/drift.py +713 -0
- refs_mcp-0.2.0/refs_mcp/contracts/models.py +349 -0
- refs_mcp-0.2.0/refs_mcp/contracts/passthrough.py +279 -0
- refs_mcp-0.2.0/refs_mcp/contracts/runtime.py +255 -0
- refs_mcp-0.2.0/refs_mcp/contracts/scrape_gh.py +225 -0
- refs_mcp-0.2.0/refs_mcp/contracts/scrape_git.py +489 -0
- refs_mcp-0.2.0/refs_mcp/contracts/scrape_rg.py +319 -0
- refs_mcp-0.2.0/refs_mcp/correlation.py +63 -0
- refs_mcp-0.2.0/refs_mcp/discovery.py +437 -0
- refs_mcp-0.2.0/refs_mcp/doctor.py +426 -0
- refs_mcp-0.2.0/refs_mcp/events.py +164 -0
- refs_mcp-0.2.0/refs_mcp/file_log.py +167 -0
- refs_mcp-0.2.0/refs_mcp/gh_models.py +92 -0
- refs_mcp-0.2.0/refs_mcp/gh_runner.py +256 -0
- refs_mcp-0.2.0/refs_mcp/git_ops.py +326 -0
- refs_mcp-0.2.0/refs_mcp/git_runner.py +485 -0
- refs_mcp-0.2.0/refs_mcp/git_url.py +74 -0
- refs_mcp-0.2.0/refs_mcp/github_schema.py +65 -0
- refs_mcp-0.2.0/refs_mcp/help_doc.py +676 -0
- refs_mcp-0.2.0/refs_mcp/host_tools.py +1086 -0
- refs_mcp-0.2.0/refs_mcp/index_render.py +151 -0
- refs_mcp-0.2.0/refs_mcp/init/__init__.py +10 -0
- refs_mcp-0.2.0/refs_mcp/init/_common.py +251 -0
- refs_mcp-0.2.0/refs_mcp/init/_vscode_shared.py +166 -0
- refs_mcp-0.2.0/refs_mcp/init/cmd_claude.py +111 -0
- refs_mcp-0.2.0/refs_mcp/init/cmd_codex.py +201 -0
- refs_mcp-0.2.0/refs_mcp/init/cmd_copilot.py +117 -0
- refs_mcp-0.2.0/refs_mcp/init/cmd_copilot_agent.py +137 -0
- refs_mcp-0.2.0/refs_mcp/init/cmd_gemini.py +109 -0
- refs_mcp-0.2.0/refs_mcp/init/cmd_opencode.py +122 -0
- refs_mcp-0.2.0/refs_mcp/init/cmd_vscode.py +65 -0
- refs_mcp-0.2.0/refs_mcp/init/cmd_vscode_insiders.py +56 -0
- refs_mcp-0.2.0/refs_mcp/models.py +1119 -0
- refs_mcp-0.2.0/refs_mcp/operations.py +677 -0
- refs_mcp-0.2.0/refs_mcp/path_safety.py +183 -0
- refs_mcp-0.2.0/refs_mcp/preseed.py +191 -0
- refs_mcp-0.2.0/refs_mcp/ranking.py +141 -0
- refs_mcp-0.2.0/refs_mcp/remote_discover.py +511 -0
- refs_mcp-0.2.0/refs_mcp/reorg.py +503 -0
- refs_mcp-0.2.0/refs_mcp/replay.py +75 -0
- refs_mcp-0.2.0/refs_mcp/run_metadata.py +410 -0
- refs_mcp-0.2.0/refs_mcp/runner.py +312 -0
- refs_mcp-0.2.0/refs_mcp/search.py +1586 -0
- refs_mcp-0.2.0/refs_mcp/selftest.py +410 -0
- refs_mcp-0.2.0/refs_mcp/server.py +813 -0
- refs_mcp-0.2.0/refs_mcp/symbol_extraction.py +674 -0
- refs_mcp-0.2.0/refs_mcp/symbols.py +557 -0
- refs_mcp-0.2.0/refs_mcp/trace_export.py +110 -0
- refs_mcp-0.2.0/refs_mcp/user_config.py +329 -0
- refs_mcp-0.2.0/tests/__init__.py +0 -0
- refs_mcp-0.2.0/tests/_otel_isolation.py +144 -0
- refs_mcp-0.2.0/tests/cli/__init__.py +0 -0
- refs_mcp-0.2.0/tests/cli/__snapshots__/test_doctor.ambr +24 -0
- refs_mcp-0.2.0/tests/cli/__snapshots__/test_help.ambr +341 -0
- refs_mcp-0.2.0/tests/cli/__snapshots__/test_init_print.ambr +119 -0
- refs_mcp-0.2.0/tests/cli/conftest.py +56 -0
- refs_mcp-0.2.0/tests/cli/test_doctor.py +76 -0
- refs_mcp-0.2.0/tests/cli/test_help.py +127 -0
- refs_mcp-0.2.0/tests/cli/test_init_print.py +55 -0
- refs_mcp-0.2.0/tests/conftest.py +193 -0
- refs_mcp-0.2.0/tests/contracts/__init__.py +0 -0
- refs_mcp-0.2.0/tests/contracts/conftest.py +28 -0
- refs_mcp-0.2.0/tests/contracts/test_absolute_path_discipline.py +252 -0
- refs_mcp-0.2.0/tests/contracts/test_gh_shape.py +86 -0
- refs_mcp-0.2.0/tests/contracts/test_git_shape.py +115 -0
- refs_mcp-0.2.0/tests/contracts/test_rg_shape.py +188 -0
- refs_mcp-0.2.0/tests/divergence_registry.toml +239 -0
- refs_mcp-0.2.0/tests/mcp/__init__.py +0 -0
- refs_mcp-0.2.0/tests/mcp/conftest.py +102 -0
- refs_mcp-0.2.0/tests/mcp/test_initialize.py +71 -0
- refs_mcp-0.2.0/tests/mcp/test_prompts.py +39 -0
- refs_mcp-0.2.0/tests/mcp/test_resources.py +58 -0
- refs_mcp-0.2.0/tests/mcp/test_tools.py +79 -0
- refs_mcp-0.2.0/tests/test_action_coverage.py +481 -0
- refs_mcp-0.2.0/tests/test_auto_clone.py +36 -0
- refs_mcp-0.2.0/tests/test_bench.py +251 -0
- refs_mcp-0.2.0/tests/test_bootstrap.py +238 -0
- refs_mcp-0.2.0/tests/test_discovery.py +164 -0
- refs_mcp-0.2.0/tests/test_exec_safety.py +130 -0
- refs_mcp-0.2.0/tests/test_extra_tools.py +657 -0
- refs_mcp-0.2.0/tests/test_extractor_honesty.py +232 -0
- refs_mcp-0.2.0/tests/test_feature_gate.py +163 -0
- refs_mcp-0.2.0/tests/test_file_log.py +141 -0
- refs_mcp-0.2.0/tests/test_git_ops_native.py +204 -0
- refs_mcp-0.2.0/tests/test_git_url.py +64 -0
- refs_mcp-0.2.0/tests/test_github_schema.py +41 -0
- refs_mcp-0.2.0/tests/test_help.py +164 -0
- refs_mcp-0.2.0/tests/test_host_tools.py +262 -0
- refs_mcp-0.2.0/tests/test_init_common.py +300 -0
- refs_mcp-0.2.0/tests/test_link.py +185 -0
- refs_mcp-0.2.0/tests/test_npm_packaging.py +400 -0
- refs_mcp-0.2.0/tests/test_otel.py +198 -0
- refs_mcp-0.2.0/tests/test_path_assertion_gate.py +283 -0
- refs_mcp-0.2.0/tests/test_path_safety.py +127 -0
- refs_mcp-0.2.0/tests/test_platform_contracts.py +560 -0
- refs_mcp-0.2.0/tests/test_privilege_gate.py +362 -0
- refs_mcp-0.2.0/tests/test_ranking.py +95 -0
- refs_mcp-0.2.0/tests/test_regex_gate.py +307 -0
- refs_mcp-0.2.0/tests/test_reorg_native.py +86 -0
- refs_mcp-0.2.0/tests/test_resource_prompt_coverage.py +481 -0
- refs_mcp-0.2.0/tests/test_run_metadata_redactor.py +132 -0
- refs_mcp-0.2.0/tests/test_runner.py +41 -0
- refs_mcp-0.2.0/tests/test_runners.py +93 -0
- refs_mcp-0.2.0/tests/test_search.py +337 -0
- refs_mcp-0.2.0/tests/test_search_argv_shape.py +436 -0
- refs_mcp-0.2.0/tests/test_search_modes_wave6b.py +443 -0
- refs_mcp-0.2.0/tests/test_server.py +142 -0
- refs_mcp-0.2.0/tests/test_streaming.py +95 -0
- refs_mcp-0.2.0/tests/test_subprocess_gate.py +278 -0
- refs_mcp-0.2.0/tests/test_symbol_extraction_helpers.py +44 -0
- refs_mcp-0.2.0/tests/test_symbols.py +227 -0
- refs_mcp-0.2.0/tests/test_symbols_escape.py +64 -0
- refs_mcp-0.2.0/tests/test_tool_shapes.py +336 -0
- refs_mcp-0.2.0/tests/test_user_config.py +441 -0
- refs_mcp-0.2.0/uv.lock +2012 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Whitelist-mode .gitignore for refs/
|
|
2
|
+
#
|
|
3
|
+
# Rule: everything at the top level is ignored except entries explicitly
|
|
4
|
+
# un-ignored with `!` below. GitHub owner directories (emerged from reorg.sh
|
|
5
|
+
# discovery) are NOT listed here — reorg.sh adds them dynamically to its
|
|
6
|
+
# in-memory allowlist at runtime. Everything else at top-level is treated
|
|
7
|
+
# as "work product" and relocated out of refs/ on --apply.
|
|
8
|
+
#
|
|
9
|
+
# To keep a new top-level file in refs/, add a `!/filename` line below.
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
|
|
13
|
+
# ── Our tooling ─────────────────────────────────
|
|
14
|
+
!/reorg.sh
|
|
15
|
+
!/test-reorg.sh
|
|
16
|
+
!/status.sh
|
|
17
|
+
!/update.sh
|
|
18
|
+
|
|
19
|
+
# ── Agent rule doc (Codex/Cursor/Claude convention) ─────────────
|
|
20
|
+
!/AGENTS.md
|
|
21
|
+
!/README.md
|
|
22
|
+
|
|
23
|
+
# ── Generated indexes ───────────────────────────
|
|
24
|
+
!/index.md
|
|
25
|
+
!/index.html
|
|
26
|
+
|
|
27
|
+
# ── Python MCP server (FastMCP) ─────────────────
|
|
28
|
+
!/refs_mcp/
|
|
29
|
+
!/refs.spec
|
|
30
|
+
!/tests/
|
|
31
|
+
!/.github/
|
|
32
|
+
!/pyproject.toml
|
|
33
|
+
!/uv.lock
|
|
34
|
+
!/justfile
|
|
35
|
+
|
|
36
|
+
# ── Local git hooks (lefthook) ──────────────────
|
|
37
|
+
# lefthook.yml is the shared config; -local overrides are per-developer
|
|
38
|
+
# and stay out of git.
|
|
39
|
+
!/lefthook.yml
|
|
40
|
+
/lefthook-local.yml
|
|
41
|
+
|
|
42
|
+
# ── npm publish tree (GitHub Packages) ──────────
|
|
43
|
+
# Wrapper + three platform packages staged at publish-npm time.
|
|
44
|
+
# CI fills the platform bin/ subdirs from PyInstaller artifacts; only
|
|
45
|
+
# the package.json files and bin/refs.js launcher live in the repo.
|
|
46
|
+
!/npm/
|
|
47
|
+
# Re-ignore staged binaries that the publish-npm CI job materializes.
|
|
48
|
+
# Keeping them out of git means the working tree never carries built
|
|
49
|
+
# artifacts and a stray local `npm pack` doesn't ship a stale binary.
|
|
50
|
+
/npm/refs-*/bin/refs
|
|
51
|
+
/npm/refs-*/bin/refs.exe
|
|
52
|
+
|
|
53
|
+
# Re-ignore build/test artifacts inside the allowlisted dirs above.
|
|
54
|
+
# Top-level ``.venv/``, ``build/``, ``dist/`` are already caught by the
|
|
55
|
+
# ``/*`` rule — listed only inside allowlisted paths here.
|
|
56
|
+
**/__pycache__/
|
|
57
|
+
*.pyc
|
|
58
|
+
refs_mcp/bench/results/
|
|
59
|
+
|
|
60
|
+
# ── Standard repo furniture ─────────────────────
|
|
61
|
+
!/.gitignore
|
|
62
|
+
!/.git/
|
|
63
|
+
!/.gitattributes
|
|
64
|
+
!/.editorconfig
|
|
65
|
+
!/README.md
|
refs_mcp-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: refs-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: FastMCP server for managing the refs/ reference-repo tree
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: click>=8.1
|
|
8
|
+
Requires-Dist: fastmcp>=3.3.1
|
|
9
|
+
Requires-Dist: githubkit>=0.13
|
|
10
|
+
Requires-Dist: opentelemetry-api>=1.20
|
|
11
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.20
|
|
12
|
+
Requires-Dist: opentelemetry-sdk>=1.20
|
|
13
|
+
Requires-Dist: pydantic>=2.6
|
|
14
|
+
Requires-Dist: tree-sitter-language-pack>=1.8
|
|
15
|
+
Requires-Dist: tzdata>=2024.1
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# refs
|
|
19
|
+
|
|
20
|
+
You have a `~/dev/refs/` folder too, don't you?
|
|
21
|
+
|
|
22
|
+
The one where every time you want to read someone's codebase — study
|
|
23
|
+
`drizzle-orm`'s query builder, skim `next.js` internals, grep through
|
|
24
|
+
`playwright` — you run
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
git clone https://github.com/foo/bar.git ~/dev/refs/bar
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
and then months later you've got a hundred loose checkouts at the top
|
|
31
|
+
level, half of them stale, two of them accidentally the same repo under
|
|
32
|
+
different names, and no idea which ones are your work vs. someone else's
|
|
33
|
+
code. You keep meaning to organize it. You never do. Nobody wants to
|
|
34
|
+
write a housekeeping tool for their own scratch directory.
|
|
35
|
+
|
|
36
|
+
This is that tool.
|
|
37
|
+
|
|
38
|
+
## What it does
|
|
39
|
+
|
|
40
|
+
Run `./reorg.sh --apply` and your pile becomes this:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
refs/
|
|
44
|
+
├── facebook/
|
|
45
|
+
│ └── react/
|
|
46
|
+
├── vuejs/
|
|
47
|
+
│ └── vue/
|
|
48
|
+
├── microsoft/
|
|
49
|
+
│ ├── playwright/
|
|
50
|
+
│ └── ...
|
|
51
|
+
└── ... (one directory per GitHub owner)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
No matter where a clone was before — flat at top, nested in a
|
|
55
|
+
mis-named folder, duplicated under two names — it ends up at
|
|
56
|
+
`<owner>/<repo>/` based on its actual `git remote`. Duplicate clones of
|
|
57
|
+
the same upstream get parked aside for review instead of clobbering.
|
|
58
|
+
Random non-repo content (your notes, binaries, scratch dirs) gets evicted
|
|
59
|
+
out of the tree entirely. Empty pseudo-owner folders get pruned.
|
|
60
|
+
|
|
61
|
+
Every move is journaled. `./reorg.sh undo --apply` reverses the most
|
|
62
|
+
recent run. Nothing magical — plain `mv`, `rmdir`, `git`.
|
|
63
|
+
|
|
64
|
+
## The daily loop
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
./reorg.sh --apply # you cloned some more stuff; put it where it belongs
|
|
68
|
+
./status.sh # quick look at what's dirty and what has updates
|
|
69
|
+
./update.sh # git pull every clean repo with remote changes
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## About the git repo you're looking at
|
|
73
|
+
|
|
74
|
+
This directory is itself a git repo, but it doesn't track the hundreds of
|
|
75
|
+
cloned reference repos — those are checkouts you can always re-clone. It
|
|
76
|
+
only tracks the tooling (`reorg.sh`, `status.sh`, `update.sh`) plus the
|
|
77
|
+
two generated indexes (`index.md`, `index.html`). A whitelist-mode
|
|
78
|
+
[`.gitignore`](.gitignore) enforces this. More on that below.
|
|
79
|
+
|
|
80
|
+
## Tools
|
|
81
|
+
|
|
82
|
+
### `reorg.sh`
|
|
83
|
+
|
|
84
|
+
Reorganizes loose clones into the `<owner>/<repo>/` layout, keeps an index
|
|
85
|
+
(`index.md`, `index.html`) up to date, evicts non-repo content out of the
|
|
86
|
+
collection, and journals every move so you can reverse it.
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
./reorg.sh # dry-run preview
|
|
90
|
+
./reorg.sh --apply # perform moves, write journal
|
|
91
|
+
./reorg.sh undo --apply # reverse the most recent apply
|
|
92
|
+
./reorg.sh --help # full option list
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Destinations (overridable):
|
|
96
|
+
|
|
97
|
+
| purpose | default path |
|
|
98
|
+
| --- | --- |
|
|
99
|
+
| duplicate-URL losers | `$(dirname $ROOT)/!CONFLICTS-RESOLVE/` |
|
|
100
|
+
| non-repo top-level content | `$(dirname $ROOT)/!WORK-PRODUCT-FROM-REFS/` |
|
|
101
|
+
| apply journals | `${XDG_STATE_HOME:-~/.local/state}/reorg/<basename-of-ROOT>/` |
|
|
102
|
+
|
|
103
|
+
### `status.sh`
|
|
104
|
+
|
|
105
|
+
Reports dirty/clean + has-updates for every repo in the tree. Walks depth 1
|
|
106
|
+
and 2, so both loose-at-top and `<owner>/<repo>/` layouts are covered.
|
|
107
|
+
|
|
108
|
+
### `update.sh`
|
|
109
|
+
|
|
110
|
+
`git pull` every clean repo that has remote changes. Skips dirty repos with
|
|
111
|
+
a note.
|
|
112
|
+
|
|
113
|
+
### `test-reorg.sh`
|
|
114
|
+
|
|
115
|
+
Integration test harness for `reorg.sh`. Runs `shellcheck`, then 42 tests
|
|
116
|
+
over synthetic git fixtures (fake `.git/config` files — no network, no
|
|
117
|
+
real repos needed).
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
./test-reorg.sh # full suite
|
|
121
|
+
./test-reorg.sh --no-shellcheck # skip the lint step
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## MCP server (FastMCP)
|
|
125
|
+
|
|
126
|
+
`refs_mcp/` ships a [FastMCP][fastmcp] server that exposes the same
|
|
127
|
+
operations as MCP tools, with structured outputs typed by Pydantic. Useful
|
|
128
|
+
when an agent wants to inspect or operate on the tree through the
|
|
129
|
+
[Model Context Protocol][mcp] instead of shelling out.
|
|
130
|
+
|
|
131
|
+
### Install + run
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
uv sync --frozen # install deps from uv.lock
|
|
135
|
+
uv run --frozen refs stdio # serve over MCP stdio
|
|
136
|
+
uv run --frozen refs http # serve over streamable-HTTP (daemon)
|
|
137
|
+
uv run --frozen refs --help # full CLI surface
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The CLI is a [click][click] group with two transport subcommands —
|
|
141
|
+
`stdio` for Claude Desktop / IDE clients that spawn the binary, and
|
|
142
|
+
`http` for daemons under systemd / Docker / k8s. Anchored to the
|
|
143
|
+
canonical pattern in
|
|
144
|
+
[`../refs/modelcontextprotocol/servers/src/git/`][mcp-git-ref] and
|
|
145
|
+
[`../refs/github/github-mcp-server/`][gh-mcp-ref].
|
|
146
|
+
|
|
147
|
+
Or build a frozen binary and invoke it directly:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
uv run --frozen python -m refs_mcp._build # produces dist/refs.exe
|
|
151
|
+
./dist/refs stdio # no Python needed at run time
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The build wrapper installs a `logging.Handler` on PyInstaller's logger
|
|
155
|
+
that `sys.exit`s on every record at WARNING or above — warnings genuinely
|
|
156
|
+
fail the build. There is no `--werror` flag in PyInstaller or uv
|
|
157
|
+
(verified via JiT reads of both upstreams).
|
|
158
|
+
|
|
159
|
+
The canonical dev commands use `uv run --frozen` so dependency
|
|
160
|
+
resolution is locked to `uv.lock` and uv never rewrites the lockfile as
|
|
161
|
+
a side effect:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
uv run --frozen pytest tests/ # 177 tests
|
|
165
|
+
uv run --frozen ruff format refs_mcp tests # format
|
|
166
|
+
uv run --frozen ruff check refs_mcp tests # lint
|
|
167
|
+
uv run --frozen pyright refs_mcp # static type check
|
|
168
|
+
uv run --frozen refs selftest # live MCP smoke (in-process)
|
|
169
|
+
uv run --frozen refs selftest --binary dist/refs.exe # vs the frozen binary
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
CI is a three-tier pipeline in `.github/workflows/ci.yml`, fronted by
|
|
173
|
+
a tiny `changes` gate. Capability is preserved across tiers — macOS,
|
|
174
|
+
Python 3.11/3.13, PyInstaller build, MCP stdio smoke, artifact upload,
|
|
175
|
+
and release upload are all defined in the workflow. What varies per
|
|
176
|
+
event is which tier fires AND whether the cheap default tier fires at
|
|
177
|
+
all on PR/branch-push (a docs-only change skips it entirely — Windows
|
|
178
|
+
never starts).
|
|
179
|
+
|
|
180
|
+
| Event | `changes` | `test` | `test-full` | `build` | `release` |
|
|
181
|
+
|---|:---:|:---:|:---:|:---:|:---:|
|
|
182
|
+
| pull_request to `main` (code change) | ✓ | ✓ | — | — | — |
|
|
183
|
+
| pull_request to `main` (docs only) | ✓ | — | — | — | — |
|
|
184
|
+
| push to `main` (code change) | ✓ | ✓ | — | — | — |
|
|
185
|
+
| push to `main` (docs only) | ✓ | — | — | — | — |
|
|
186
|
+
| `workflow_dispatch` with `full-ci: true` | — | ✓ | ✓ | ✓ | — |
|
|
187
|
+
| push tag `v*` | — | ✓ | ✓ | ✓ | ✓ |
|
|
188
|
+
|
|
189
|
+
**`changes`** is a cheap ubuntu probe (`dorny/paths-filter@v4`) that
|
|
190
|
+
flags whether this push touched any runtime-relevant file:
|
|
191
|
+
`refs_mcp/**`, `tests/**`, `pyproject.toml`,
|
|
192
|
+
`uv.lock`, `refs.spec`, `.github/workflows/ci.yml`, or any `*.sh`.
|
|
193
|
+
If only docs/comments/README changed, every downstream tier skips and
|
|
194
|
+
the PR's required check passes without spinning up Windows. The gate
|
|
195
|
+
itself is bypassed on `workflow_dispatch` + tag push (the operator
|
|
196
|
+
explicitly asked for the full run).
|
|
197
|
+
|
|
198
|
+
**`test`** is the default cheap signal — `ubuntu-latest` and
|
|
199
|
+
`windows-latest` on Python 3.12 only. Linux runs ruff + pyright +
|
|
200
|
+
shellcheck + bash `test-reorg.sh` + pytest; Windows runs ruff +
|
|
201
|
+
pytest. No macOS, no extra Python versions.
|
|
202
|
+
|
|
203
|
+
**`test-full`** is extra source-pytest coverage: `{ubuntu, windows}`
|
|
204
|
+
× `{3.11, 3.13}` + `macos-latest/3.12`. Source pytest only, no extra
|
|
205
|
+
gates (pyright + shellcheck are version-independent and already
|
|
206
|
+
covered by the canonical `test` cell).
|
|
207
|
+
|
|
208
|
+
**`build`** produces per-OS PyInstaller binaries pinned to py3.13
|
|
209
|
+
(the shipped interpreter), runs a CLI smoke + the full 6-tool live
|
|
210
|
+
MCP stdio smoke against three shallow-cloned target repos
|
|
211
|
+
(`MicrosoftDocs/mcp` + `BurntSushi/ripgrep` +
|
|
212
|
+
`modelcontextprotocol/python-sdk`), and uploads each binary as an
|
|
213
|
+
artifact (retention 7 days for non-tag runs).
|
|
214
|
+
|
|
215
|
+
**`release`** downloads the three per-OS binaries and attaches them
|
|
216
|
+
to a GitHub release via [`softprops/action-gh-release@v3`][gh-release].
|
|
217
|
+
|
|
218
|
+
To run the full sweep manually (e.g. before cutting a release), use
|
|
219
|
+
the Actions tab and dispatch the `ci` workflow with `full-ci: true`.
|
|
220
|
+
|
|
221
|
+
Set `REFS_ROOT` to point at a tree other than the current directory.
|
|
222
|
+
`REFS_CONFLICTS_DIR`, `REFS_WORK_PRODUCT_DIR`, and `REFS_JOURNAL_DIR`
|
|
223
|
+
override the matching reorg.sh defaults.
|
|
224
|
+
|
|
225
|
+
Add to a Claude Code project's `.mcp.json`:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"mcpServers": {
|
|
230
|
+
"refs": {
|
|
231
|
+
"command": "uv",
|
|
232
|
+
"args": ["run", "refs", "stdio"],
|
|
233
|
+
"cwd": "<absolute path to your refs/ tree>"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Or, when the frozen binary is on PATH:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"mcpServers": {
|
|
244
|
+
"refs": {
|
|
245
|
+
"command": "refs",
|
|
246
|
+
"args": ["stdio"],
|
|
247
|
+
"cwd": "<absolute path to your refs/ tree>"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Tools
|
|
254
|
+
|
|
255
|
+
Evidence-graded search + symbol extraction (read-only by construction):
|
|
256
|
+
|
|
257
|
+
| Tool | Read-only | Implementation |
|
|
258
|
+
| -------------------------- | --------- | -------------- |
|
|
259
|
+
| `refs_find_repo` | yes | Bounded-scope repo lookup by `owner/repo`, exact name, or fuzzy substring. |
|
|
260
|
+
| `refs_search_evidence` | yes | `rg --json` content search with verdict labels (MATCH / VALIDATED_EMPTY / INVALID_EMPTY / TRUNCATED / FAILED / SKIPPED_UNSAFE). |
|
|
261
|
+
| `refs_prove_absence` | yes | Single-pass literal probe + positive control; strict VALIDATED_EMPTY or FAILED. |
|
|
262
|
+
| `refs_inspect_terms` | yes | Batch verdict map over a list of terms in one repo. |
|
|
263
|
+
| `refs_list_symbols` | yes | Language-agnostic symbol extraction via [`tree-sitter-language-pack`][tslp] (306 languages) with `DEFINED_AND_TESTED` / `DEFINED_ONLY` / `DEFINED_PRIVATE` verdicts. |
|
|
264
|
+
| `refs_find_symbol` | yes | Resolve one symbol's signature + tests + head SHA across any supported language. |
|
|
265
|
+
| `refs_host_tools` | yes | Probe-based inventory of host CLIs (rg, git, gh, …) + Python-module gates (tree-sitter) with feature-gate map. |
|
|
266
|
+
| `refs_discover_remote` | yes | `gh search repos` for cross-org discovery (needs `gh` on PATH). |
|
|
267
|
+
| `refs_discover_remote_org` | yes | `gh repo list <owner>` for enumerating an org's repos. |
|
|
268
|
+
| `refs_preseed_run` | no | Auto-clone the curated upstream-reference set (FastMCP, MCP SDK + spec, Pydantic, ripgrep, OTel semconv, pgr, MicrosoftDocs/mcp). |
|
|
269
|
+
|
|
270
|
+
### Symbol extraction (language-agnostic)
|
|
271
|
+
|
|
272
|
+
`refs_list_symbols` and `refs_find_symbol` route per-file to an
|
|
273
|
+
extractor stack modeled on the [Sourcegraph + GitHub code-nav
|
|
274
|
+
architecture][scip]:
|
|
275
|
+
|
|
276
|
+
| Tier | Extractor | Status |
|
|
277
|
+
| ------------- | -------------------------------------------------- | ------ |
|
|
278
|
+
| **primary** | tree-sitter via [`tree-sitter-language-pack`][tslp] (306 languages) | shipping |
|
|
279
|
+
| **enrichment** | SCIP / LSP semantic indexes — override structural records with semantic-precise resolution (overloaded / imported / external symbols). | protocol locked; future work |
|
|
280
|
+
| **fallback** | Universal Ctags subprocess — for languages tree-sitter doesn't ship a grammar for | protocol locked; future work |
|
|
281
|
+
|
|
282
|
+
The `Symbol` IR carries `name`, `kind` (function / async_function /
|
|
283
|
+
method / class / struct / enum / trait / interface / type_alias / impl
|
|
284
|
+
/ constant / variable), `language`, `signature`, `line`, `end_line`,
|
|
285
|
+
`visibility`, `parent`, `decorators`, `extractor` (which extractor
|
|
286
|
+
produced this record — useful for verdict explainability), plus the
|
|
287
|
+
`DEFINED_AND_TESTED` / `DEFINED_ONLY` / `DEFINED_PRIVATE` / `NOT_FOUND`
|
|
288
|
+
verdict.
|
|
289
|
+
|
|
290
|
+
Across the smoke targets, tree-sitter extracts:
|
|
291
|
+
|
|
292
|
+
| Repo | Language | Symbols |
|
|
293
|
+
| ----------------------------------- | ------------ | ------- |
|
|
294
|
+
| `modelcontextprotocol/python-sdk` | Python | 631 |
|
|
295
|
+
| `MicrosoftDocs/mcp` | TypeScript | 100 |
|
|
296
|
+
| `BurntSushi/ripgrep` | Rust | 3472 |
|
|
297
|
+
|
|
298
|
+
Tree management (every tool runs in native Python or git/gh subprocess — no shell wrappers):
|
|
299
|
+
|
|
300
|
+
| Tool | Read-only | Implementation |
|
|
301
|
+
| -------------------------- | --------- | -------------- |
|
|
302
|
+
| `refs_help` | yes | Returns the typed `HelpDocument` — read this first per session. |
|
|
303
|
+
| `refs_status` | yes | Filesystem walk + `configparser` on `.git/config`. Offline. |
|
|
304
|
+
| `refs_discover` | yes | Same walk; optionally writes `$REFS_JOURNAL_DIR/refs.inventory.json`. |
|
|
305
|
+
| `refs_reorg_preview` | yes | Pure planner — builds `ReorgPlan` from typed inventory, no IO. |
|
|
306
|
+
| `refs_reorg_apply` | no | `pathlib` + `shutil.move` + `csv.writer` for the TSV journal. |
|
|
307
|
+
| `refs_reorg_undo` | no | `csv.reader` reads the journal; moves reversed with `shutil`. |
|
|
308
|
+
| `refs_clone` | no | `gh repo clone` (github.com, auth-aware) or `git clone`; dry-run default; refuses non-GitHub and overwrites. |
|
|
309
|
+
| `refs_update_check` | yes | `git status --porcelain=v2 --branch` + `git ls-remote` for `has_updates`. |
|
|
310
|
+
| `refs_update_apply` | no | `git stash push` → `git pull --ff-only` → `git stash pop`; per-repo concurrency. |
|
|
311
|
+
| `refs_degit_export` | no | `git archive` into an export dir; source clone untouched. |
|
|
312
|
+
| `refs_sparse_materialize` | no | `git sparse-checkout set`; traversal patterns refused. |
|
|
313
|
+
| `refs_index_generate` | no | Pure-Python renderer of `index.md` + `index.html` from typed `Inventory`. |
|
|
314
|
+
| `refs_journal_latest` | yes | `csv.reader` over the latest TSV journal. |
|
|
315
|
+
| `refs_events_tail` | yes | Read the JSONL operation events log. |
|
|
316
|
+
|
|
317
|
+
### Feature gates (host_tools probe)
|
|
318
|
+
|
|
319
|
+
Each tool that depends on a host CLI is gated by the bootstrap
|
|
320
|
+
`host_tools` probe — `shutil.which` is not enough; the probe **runs**
|
|
321
|
+
each tool's `--version` and only marks it OK on success. Features that
|
|
322
|
+
need an absent tool return a typed `FAILED` verdict with the install
|
|
323
|
+
hint, not a silent zero. See `refs_host_tools` for the live snapshot.
|
|
324
|
+
|
|
325
|
+
Per-feature gates land in `refs_mcp.host_tools._CURATED_FEATURES`:
|
|
326
|
+
|
|
327
|
+
| Feature | Required tool(s) | Used by |
|
|
328
|
+
| ------------------------ | --------------------------- | ------- |
|
|
329
|
+
| `content_search` | `rg` | `refs_search_evidence`, `refs_prove_absence`, `refs_inspect_terms` |
|
|
330
|
+
| `file_enum` | `rg` | Positive-control probes, corpus building |
|
|
331
|
+
| `structural_symbols` | Python `tree_sitter_language_pack` | `refs_list_symbols`, `refs_find_symbol` (gated on Python import, not a CLI) |
|
|
332
|
+
| `auto_clone` | `gh` (preferred) or `git` | `refs_mcp.auto_clone.ensure_repo` |
|
|
333
|
+
| `git_status` | `git` | `refs_update_check` sweep |
|
|
334
|
+
| `git_pull` / `git_stash` | `git` | `refs_update_apply` sweep |
|
|
335
|
+
| `git_archive_local` | `git` | `refs_degit_export` |
|
|
336
|
+
| `git_sparse_checkout` | `git` | `refs_sparse_materialize` |
|
|
337
|
+
| `remote_repo_search` | `gh` | `refs_discover_remote` |
|
|
338
|
+
| `remote_org_list` | `gh` | `refs_discover_remote_org` |
|
|
339
|
+
| `remote_archive_tarball` | `gh` | Future: archive without local clone (`gh api repos/{o}/{r}/tarball/{ref}`) |
|
|
340
|
+
| `remote_metadata` | `gh` | Default-branch / head SHA / disk size without clone |
|
|
341
|
+
| `pre_clone_size_inspection` | `gh` | `refs_discover_remote` populates `DiscoverableRepo.size_kb` |
|
|
342
|
+
|
|
343
|
+
### `~/.refs/` layout (XDG-friendly)
|
|
344
|
+
|
|
345
|
+
Bootstrap writes per-run artifacts to a user-level layout (overridable
|
|
346
|
+
via env vars; defaults follow the XDG Base Directory spec on Linux).
|
|
347
|
+
Actual on-disk layout as implemented in `refs_mcp.user_config.UserPaths`:
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
~/.refs/
|
|
351
|
+
├── .refs.toml # user preferences (auto_clone_allowed_hosts, etc.)
|
|
352
|
+
├── repos/ # default refs_root (cloned upstreams)
|
|
353
|
+
│ └── <owner>/<repo>/
|
|
354
|
+
├── state/
|
|
355
|
+
│ ├── events.jsonl # structured operation events
|
|
356
|
+
│ ├── server-runs.jsonl # one entry per server boot (run_id, transport, env)
|
|
357
|
+
│ ├── logs/ # date-rotated structured file logs (trace_id-correlated)
|
|
358
|
+
│ └── traces/ # OTel spans as JSONL per run (file exporter)
|
|
359
|
+
├── data/
|
|
360
|
+
│ └── replay/ # captured request/response artifacts per run
|
|
361
|
+
└── cache/
|
|
362
|
+
└── symbol-index/ # symbol-index cache keyed by (repo_path, head_sha)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Override individual subtrees with `XDG_STATE_HOME` / `XDG_DATA_HOME` /
|
|
366
|
+
`XDG_CACHE_HOME` (each gets `/refs` appended), or the more-specific
|
|
367
|
+
`REFS_STATE_DIR` / `REFS_DATA_DIR` / `REFS_CACHE_DIR`. Setting
|
|
368
|
+
`REFS_HOME` overrides the **whole tree** — XDG vars are ignored when
|
|
369
|
+
REFS_HOME is set so the layout stays cohesive (no surprise splits). The
|
|
370
|
+
config file (`.refs.toml`) lives directly under the home root; relocate
|
|
371
|
+
it via `REFS_HOME`.
|
|
372
|
+
|
|
373
|
+
The shell scripts (`reorg.sh`, `status.sh`, `update.sh`) remain in the
|
|
374
|
+
repo for shell-CLI users. The MCP server's reorg/status/update paths do
|
|
375
|
+
not call them — they share the on-disk contract (whitelist `.gitignore`,
|
|
376
|
+
TSV journal format, `<owner>/<repo>` layout) so a journal written by
|
|
377
|
+
either path is reversible by the other.
|
|
378
|
+
|
|
379
|
+
### Resources
|
|
380
|
+
|
|
381
|
+
| URI | MIME | Content |
|
|
382
|
+
| ------------------------------ | -------------------------- | ------- |
|
|
383
|
+
| `refs://inventory` | `application/json` | Structured `Inventory` of the tree (typed `ResourceResult`). |
|
|
384
|
+
| `refs://inventory/schema` | `application/schema+json` | JSON Schema of the `Inventory` model. |
|
|
385
|
+
| `refs://repo/{owner}/{repo}` | `application/json` | One typed `RepoRecord` by owner+repo. |
|
|
386
|
+
| `refs://config` | `application/json` | Sanitized server config (no secrets). |
|
|
387
|
+
| `refs://journal/latest` | `application/json` | Latest reorg journal, parsed. |
|
|
388
|
+
| `refs://events/latest` | `application/x-ndjson` | Tail of the structured operation events log (NDJSON). |
|
|
389
|
+
| `refs://index.md` | `text/markdown` | `index.md` rendered from typed `Inventory`. |
|
|
390
|
+
| `refs://index.html` | `text/html` | `index.html` rendered from typed `Inventory`. |
|
|
391
|
+
| `refs://help` | `application/json` | Typed `HelpDocument` — same content as the `refs_help` tool. |
|
|
392
|
+
| `refs://help/markdown` | `text/markdown` | Help rendered as markdown for humans. |
|
|
393
|
+
|
|
394
|
+
### Prompts
|
|
395
|
+
|
|
396
|
+
| Name | Purpose |
|
|
397
|
+
| ------------------------------- | ------- |
|
|
398
|
+
| `refs-agent-onboarding` | Read-this-first onboarding for any agent connecting to the server. |
|
|
399
|
+
| `audit-refs-tree` | Audit-before-mutate workflow. |
|
|
400
|
+
| `clone-reference-repo` | Safe clone workflow (refs_clone preview → apply → verify). |
|
|
401
|
+
| `refresh-reference-tree` | status → check → update sequence. |
|
|
402
|
+
| `prepare-agent-reference-pack` | Surface local upstream docs from the inventory for a given topic. |
|
|
403
|
+
| `export-plain-source-snapshot` | degit-style snapshot via `refs_degit_export`. |
|
|
404
|
+
|
|
405
|
+
### Help surface (lessons baked into the server)
|
|
406
|
+
|
|
407
|
+
`refs_help` and the two `refs://help[/markdown]` resources expose a typed
|
|
408
|
+
guide for anyone — operator or agent — about to use this server. The
|
|
409
|
+
content is generic and transferable: which tools are offline vs
|
|
410
|
+
network-bound, why generated indexes are never parsed as data, what the
|
|
411
|
+
journal contract is for undo, why the OpenTelemetry tracer is a no-op
|
|
412
|
+
without an exporter, why owner/repo names with `.`, `..`, control chars,
|
|
413
|
+
or Windows reserved names are rejected, and which upstream docs are
|
|
414
|
+
already cloned under the refs root. Tip severities (`info` / `warning`
|
|
415
|
+
/ `critical`) match how aggressively a client should surface them.
|
|
416
|
+
|
|
417
|
+
### Generated indexes are not canonical
|
|
418
|
+
|
|
419
|
+
`index.md` and `index.html` are presentation artifacts produced by
|
|
420
|
+
`reorg.sh`. The MCP server's source of truth is the structured inventory
|
|
421
|
+
built from a filesystem walk + `.git/config` reads. No tool in this
|
|
422
|
+
package parses `index.md` or `index.html` as data.
|
|
423
|
+
|
|
424
|
+
### Mid-2026 MCP / Claude features in scope
|
|
425
|
+
|
|
426
|
+
What the server uses today:
|
|
427
|
+
|
|
428
|
+
- **Structured tool outputs and output schemas** — every tool returns a
|
|
429
|
+
Pydantic model, so FastMCP emits both `content` (legacy text JSON) and
|
|
430
|
+
`structuredContent` with a generated `outputSchema` (MCP 2025-11-25 §
|
|
431
|
+
Tool Result).
|
|
432
|
+
- **Tool annotations** — `read_only_hint`, `destructive_hint`,
|
|
433
|
+
`idempotent_hint`, `open_world_hint` are set on every tool so the
|
|
434
|
+
client can render safe-vs-destructive UI.
|
|
435
|
+
- **Resources with MIME** — JSON resources are addressable and
|
|
436
|
+
discoverable via `resources/list`.
|
|
437
|
+
- **Prompts** — `audit-refs-tree` teaches an agent the right
|
|
438
|
+
read-before-mutate workflow.
|
|
439
|
+
- **Progress + log notifications** — `refs_reorg_apply` and
|
|
440
|
+
`refs_update_apply` report bracketed progress to the MCP client via
|
|
441
|
+
`ctx.report_progress` and emit structured status via `ctx.log`. The
|
|
442
|
+
asyncio streaming runner in `refs_mcp.runner` is the canonical
|
|
443
|
+
subprocess entrypoint and remains available for any future tool that
|
|
444
|
+
needs line-by-line subprocess forwarding.
|
|
445
|
+
- **OpenTelemetry** — FastMCP emits per-call SERVER spans automatically;
|
|
446
|
+
this package adds a CLIENT-kind span around every subprocess call
|
|
447
|
+
(git, gh, ripgrep) carrying `process.command`, `process.command_args`,
|
|
448
|
+
`process.working_directory`, `process.exit.code`, duration, and
|
|
449
|
+
ERROR status on non-zero exits. Bootstrap (`refs_mcp.bootstrap`) wires
|
|
450
|
+
the `TracerProvider` once per process with a file `SpanExporter`
|
|
451
|
+
writing to `~/.refs/traces/<run_id>.jsonl` so every run has captured
|
|
452
|
+
spans on disk by default. OTLP export is also wired:
|
|
453
|
+
- `OTEL_EXPORTER_OTLP_ENDPOINT=…` adds the `OTLPSpanExporter`
|
|
454
|
+
(proto-http) via `BatchSpanProcessor`. The exporter reads the
|
|
455
|
+
standard `OTEL_EXPORTER_OTLP_*` configuration variables. The
|
|
456
|
+
dependency ships as a hard runtime dep — no `[otlp]` extra dance.
|
|
457
|
+
- `OTEL_SERVICE_NAME=…` (default `refs`) attaches a meaningful
|
|
458
|
+
`service.name` Resource attribute. W3C trace context, semconv
|
|
459
|
+
`service.*` / `host.*` / `process.*` / `os.*` attributes are
|
|
460
|
+
populated by `refs_mcp.run_metadata`.
|
|
461
|
+
|
|
462
|
+
What is deliberately not used:
|
|
463
|
+
|
|
464
|
+
- **Sampling** — server-requests-LLM. This server doesn't generate text;
|
|
465
|
+
no natural use case.
|
|
466
|
+
- **Elicitation** — destructive ops are gated by `destructive_hint` and
|
|
467
|
+
by the default-dry-run contract; a separate "ask the human" round-trip
|
|
468
|
+
adds friction without safety.
|
|
469
|
+
- **Roots** — server is launched in a specific working directory (or via
|
|
470
|
+
`REFS_ROOT`); root negotiation isn't useful when the path is part of
|
|
471
|
+
the launch contract.
|
|
472
|
+
- **Background tasks (`execution.taskSupport`, SEP-1686)** — would
|
|
473
|
+
require a runner like Docket; advertised once an MVP integrates one.
|
|
474
|
+
- **Resource subscriptions / `subscriptions/listen`** — useful for
|
|
475
|
+
watching inventory changes but out of scope for the initial drop.
|
|
476
|
+
|
|
477
|
+
### Tests
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
uv run pytest tests/
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
The pytest suite covers path safety, GitHub URL parsing, subprocess
|
|
484
|
+
runner (including timeout and missing-executable paths), discovery on a
|
|
485
|
+
synthetic tree, native reorg / git_ops / operations behavior over the
|
|
486
|
+
git/gh subprocess paths (status-sweep, update-sweep, clone, archive,
|
|
487
|
+
sparse-checkout), verdict-graded search + symbol extraction, host_tools
|
|
488
|
+
probe + feature-gate derivation, auto-clone allowlist + path safety,
|
|
489
|
+
MCP server smoke (tools/resources/prompts list, structured inventory
|
|
490
|
+
return, annotation presence), OpenTelemetry span emission, and the
|
|
491
|
+
benchmark harness.
|
|
492
|
+
|
|
493
|
+
The pre-existing `./test-reorg.sh` Bash suite remains authoritative for
|
|
494
|
+
filesystem moves, conflicts, eviction, and journal/undo semantics.
|
|
495
|
+
|
|
496
|
+
[fastmcp]: https://github.com/jlowin/fastmcp
|
|
497
|
+
[mcp]: https://modelcontextprotocol.io/specification
|
|
498
|
+
[click]: https://github.com/pallets/click
|
|
499
|
+
[mcp-git-ref]: https://github.com/modelcontextprotocol/servers/tree/main/src/git
|
|
500
|
+
[gh-mcp-ref]: https://github.com/github/github-mcp-server
|
|
501
|
+
[tslp]: https://github.com/kreuzberg-dev/tree-sitter-language-pack
|
|
502
|
+
[scip]: https://github.com/sourcegraph/scip
|
|
503
|
+
[gh-release]: https://github.com/softprops/action-gh-release
|
|
504
|
+
|
|
505
|
+
## `.gitignore` is a whitelist
|
|
506
|
+
|
|
507
|
+
`refs/.gitignore` uses whitelist-mode:
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
/*
|
|
511
|
+
!/reorg.sh
|
|
512
|
+
!/test-reorg.sh
|
|
513
|
+
...
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Everything at the top level is ignored *except* the explicitly un-ignored
|
|
517
|
+
entries. GitHub owner directories (over a hundred of them, in practice)
|
|
518
|
+
are correctly excluded — git never sees them, so no embedded-repo
|
|
519
|
+
warnings, no accidental staging.
|
|
520
|
+
|
|
521
|
+
To keep a new top-level file, add a `!/filename` line. `reorg.sh`'s
|
|
522
|
+
top-level allowlist also reads this file: anything not on the static or
|
|
523
|
+
dynamic (GitHub owner) allowlist will be evicted to
|
|
524
|
+
`!WORK-PRODUCT-FROM-REFS/` on the next `--apply`.
|