witan-code 0.3.0__tar.gz → 0.6.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.
- witan_code-0.6.0/CHANGELOG.md +97 -0
- witan_code-0.3.0/README.md → witan_code-0.6.0/PKG-INFO +75 -5
- witan_code-0.3.0/PKG-INFO → witan_code-0.6.0/README.md +45 -32
- {witan_code-0.3.0 → witan_code-0.6.0}/docs/CLI_REFERENCE.md +7 -1
- {witan_code-0.3.0 → witan_code-0.6.0}/pyproject.toml +9 -3
- witan_code-0.6.0/tests/test_cli_output_format.py +91 -0
- witan_code-0.6.0/tests/test_config.py +64 -0
- witan_code-0.6.0/tests/test_config_targets.py +247 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_elicit.py +3 -54
- witan_code-0.6.0/tests/test_graph.py +23 -0
- witan_code-0.6.0/tests/test_repo.py +87 -0
- witan_code-0.6.0/tests/test_setup.py +103 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/uv.lock +148 -62
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/bridge.py +5 -8
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/cli.py +130 -89
- witan_code-0.6.0/witan_code/config.py +214 -0
- witan_code-0.6.0/witan_code/elicit.py +54 -0
- witan_code-0.6.0/witan_code/graph.py +99 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/hooks.py +1 -1
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/indexer.py +3 -9
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/maintenance.py +26 -53
- witan_code-0.6.0/witan_code/output.py +42 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/repo.py +17 -42
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/schema/bridge-schema.pg +6 -2
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/schema/code-schema.pg +6 -1
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/server.py +3 -0
- witan_code-0.6.0/witan_code/setup.py +106 -0
- witan_code-0.3.0/CHANGELOG.md +0 -44
- witan_code-0.3.0/queries/code_read.gq +0 -152
- witan_code-0.3.0/tests/test_detach.py +0 -39
- witan_code-0.3.0/tests/test_graph.py +0 -39
- witan_code-0.3.0/tests/test_repo.py +0 -60
- witan_code-0.3.0/tests/test_setup.py +0 -266
- witan_code-0.3.0/witan_code/_detach.py +0 -27
- witan_code-0.3.0/witan_code/config.py +0 -65
- witan_code-0.3.0/witan_code/elicit.py +0 -92
- witan_code-0.3.0/witan_code/graph.py +0 -271
- witan_code-0.3.0/witan_code/queries/code_mutations.gq +0 -81
- witan_code-0.3.0/witan_code/setup.py +0 -230
- {witan_code-0.3.0 → witan_code-0.6.0}/.gitignore +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/config/claude.json +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/config/copilot.json +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/config/pi.json +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/docs/BRANCH_INDEXING.md +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/docs/EDGE_PRECISION_TIERS.md +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/docs/PACKAGE_MAP.md +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/docs/STAGE2_STITCHING.md +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/docs/SYMBOL_FORMAT.md +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/docs/SYMBOL_TABLE.md +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/docs/USER_GUIDE.md +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/install.sh +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/__init__.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/conftest.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_branches.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_bridge.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_cli_hooks.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_context.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_crossrepo.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_edges.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_enrichment.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_hooks.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_indexer.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_languages.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_maintenance.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_package_map.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_server.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_stitch.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_symbol_table.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/tests/test_visualize.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/__init__.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/__main__.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/bridge_extractors.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/context.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/edges.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/extensions/pi/codegraph.ts +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/package_map.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries/bridge.gq +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0/witan_code}/queries/code_mutations.gq +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries/code_read.gq +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries/delete.gq +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries_ts/bash.scm +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries_ts/hcl.scm +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries_ts/python.scm +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries_ts/sql.scm +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries_ts/typescript.scm +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/queries_ts/yaml.scm +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/skills/witan-code/SKILL.md +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/stitch.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/store.py +0 -0
- {witan_code-0.3.0 → witan_code-0.6.0}/witan_code/visualize.py +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `witan-code` 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 adheres to [Semantic Versioning](https://semver.org/) (pre-1.0:
|
|
7
|
+
a MINOR bump may include breaking changes).
|
|
8
|
+
|
|
9
|
+
## [0.6.0] - 2026-07-21
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`repo.detect(override=...)` and the `WITAN_REPO` env var now route
|
|
14
|
+
through `normalise`** (issue #142), same as an auto-detected git remote —
|
|
15
|
+
matching the fix in witan-council 0.6.0. Previously an explicitly-passed
|
|
16
|
+
`repo=` (or `WITAN_REPO`) was stored verbatim, bypassing
|
|
17
|
+
canonicalization. `graph_id` (the shared-cluster graph id) already
|
|
18
|
+
case-folded unconditionally and needs no change; a repo whose canonical
|
|
19
|
+
key changes case after this fix should be re-indexed (`witan-code
|
|
20
|
+
reindex`) so its per-repo store lands under the new key.
|
|
21
|
+
- Depends on `witan-core[cli]>=0.2,<1` (unchanged range; picks up 0.4.0's
|
|
22
|
+
repo-key case-fold in `normalise`, which this package's `repo.detect`
|
|
23
|
+
relies on).
|
|
24
|
+
|
|
25
|
+
## [0.5.0] - 2026-07-20
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `config.toml` + `[targets.<name>]` support (previously env-var only):
|
|
30
|
+
`WITAN_CONFIG` / `~/.config/witan/config.toml`, a global `code_dir`/
|
|
31
|
+
`author`, and named targets overriding `code_dir`/`author`, selected by
|
|
32
|
+
`WITAN_TARGET` env var, an explicit `load(target=...)`, or auto-detection
|
|
33
|
+
via `match_paths`/`match_repos`/`match_hosts`/`match_orgs` (same
|
|
34
|
+
precedence and file as witan — see `witan_core.target_config` and
|
|
35
|
+
`witan.config.load()`'s docstring). A target can carry witan's
|
|
36
|
+
`server`/`graph`/`token` alongside this server's `code_dir` under one
|
|
37
|
+
name; witan-code reads only the fields it knows. `Config` gained
|
|
38
|
+
`target_name`. `load()` now optionally takes a `target` argument (was
|
|
39
|
+
zero-argument only). New dependency: `pydantic>=2,<3`.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- Now depends on `witan-core[cli]>=0.2,<1` (unchanged range; picks up
|
|
44
|
+
0.3.0, which adds the shared target-routing logic this release uses).
|
|
45
|
+
|
|
46
|
+
## [0.4.0] - 2026-07-16
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- **`witan-code --output-format`**: `repos`, `symbols`, and `stitch` can now
|
|
51
|
+
render their table data as `json`, `toml`, or `yaml` in addition to the
|
|
52
|
+
default Rich table output. The same option is honored when mounted as
|
|
53
|
+
`witan --output-format … code …`.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Adopted the shared `witan-core` package for the CLI scaffolding
|
|
58
|
+
(`make_app`/`report_install`/`resolve_author` + agent-name constants),
|
|
59
|
+
replacing the previously duplicated copies. No user-facing behavior change.
|
|
60
|
+
Now depends on `witan-core[cli]>=0.2,<1`.
|
|
61
|
+
|
|
62
|
+
## [0.3.0] - 2026-07-10
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- **`witan-code --version`**: prints the installed package version,
|
|
67
|
+
appending a git short ref when installed editable (local workspace) or
|
|
68
|
+
directly from a VCS URL (`uvx --from git+...`), via the new
|
|
69
|
+
`agent_config_kit.resolve_version()` helper.
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
|
|
73
|
+
- `agent-config-kit` dependency bumped to `>=0.4,<1` (was `>=0.1,<1`) to
|
|
74
|
+
require the version that ships `resolve_version`, which backs the new
|
|
75
|
+
`--version` flag.
|
|
76
|
+
|
|
77
|
+
## [0.2.1] - 2026-07-09
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
|
|
81
|
+
- **Pinned `tree-sitter` to `0.25.x`**: `0.26.0` has a use-after-free in its
|
|
82
|
+
pyo3 `Node` binding that segfaults (exit 139) under the heavy
|
|
83
|
+
`.parent()`/`.children()` churn `_walk_defs` does on real-world-sized
|
|
84
|
+
files — deterministically on the 2nd-3rd parse in one process, so
|
|
85
|
+
`witan-code index` hard-crashed on any real repo. `0.25.0`-`0.25.2` are
|
|
86
|
+
crash-free with the same ABI-15 grammar wheels.
|
|
87
|
+
|
|
88
|
+
## [0.2.0] - 2026-07-09
|
|
89
|
+
|
|
90
|
+
Initial PyPI release. `witan-code` is a tree-sitter-based code graph MCP
|
|
91
|
+
server (Layer 2): indexes a repo's symbols (functions, methods, classes,
|
|
92
|
+
modules) and their relationships, then exposes `code_search_symbol` /
|
|
93
|
+
`code_find_definition` / `code_find_references` / `code_callers` /
|
|
94
|
+
`code_impact` queries to the agent. Includes the Layer 2.5 cross-repo bridge,
|
|
95
|
+
linking SOA repos by shared `env_var`/`endpoint`/`package`/`service` contract
|
|
96
|
+
keys. Mounts standalone as `witan-code` or under `witan code …` when the
|
|
97
|
+
`witan` umbrella CLI is installed alongside it.
|
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: witan-code
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: witan-code — tree-sitter code graph + cross-repo bridge (mounts under `witan code`)
|
|
5
|
+
Project-URL: Homepage, https://github.com/mitodl/agent-kit/tree/main/mcp/servers/witan-code
|
|
6
|
+
Project-URL: Repository, https://github.com/mitodl/agent-kit
|
|
7
|
+
Project-URL: Documentation, https://github.com/mitodl/agent-kit/blob/main/mcp/servers/witan-code/README.md
|
|
8
|
+
Project-URL: Issues, https://github.com/mitodl/agent-kit/issues
|
|
9
|
+
License-Expression: BSD-3-Clause
|
|
10
|
+
Keywords: agent,code-graph,coding-agent,mcp,static-analysis,tree-sitter
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Requires-Dist: agent-config-kit<1,>=0.4
|
|
14
|
+
Requires-Dist: cyclopts<5,>=4
|
|
15
|
+
Requires-Dist: fastmcp<4,>=3.4.2
|
|
16
|
+
Requires-Dist: pydantic<3,>=2
|
|
17
|
+
Requires-Dist: pyyaml<7,>=6
|
|
18
|
+
Requires-Dist: rich>=13
|
|
19
|
+
Requires-Dist: tomli-w<2,>=1
|
|
20
|
+
Requires-Dist: tree-sitter-bash<0.26,>=0.25
|
|
21
|
+
Requires-Dist: tree-sitter-go<0.26,>=0.25
|
|
22
|
+
Requires-Dist: tree-sitter-hcl<1.3,>=1.2
|
|
23
|
+
Requires-Dist: tree-sitter-python<0.26,>=0.25
|
|
24
|
+
Requires-Dist: tree-sitter-sql<0.4,>=0.3
|
|
25
|
+
Requires-Dist: tree-sitter-typescript<0.24,>=0.23
|
|
26
|
+
Requires-Dist: tree-sitter-yaml<0.8,>=0.7
|
|
27
|
+
Requires-Dist: tree-sitter<0.26,>=0.25
|
|
28
|
+
Requires-Dist: witan-core[cli]<1,>=0.2
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
1
31
|
# witan-code
|
|
2
32
|
|
|
3
33
|
> "witan" is pronounced `WIT-ən` (/ˈwɪtən/) — rhymes with "written" minus the r.
|
|
@@ -60,6 +90,22 @@ machine's local code index.
|
|
|
60
90
|
- [CLI reference](https://github.com/mitodl/agent-kit/blob/main/mcp/servers/witan-code/docs/CLI_REFERENCE.md) — every `witan-code` command with
|
|
61
91
|
its full flag table and an example invocation.
|
|
62
92
|
|
|
93
|
+
## CLI structured output
|
|
94
|
+
|
|
95
|
+
`witan-code` table-producing commands can emit machine-readable output instead
|
|
96
|
+
of Rich tables:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
witan-code --output-format json repos
|
|
100
|
+
witan-code --output-format yaml symbols --role exported
|
|
101
|
+
witan --output-format toml code stitch --unresolved
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Supported formats are `txt` (default), `json`, `toml`, and `yaml`. The same
|
|
105
|
+
setting is available through `WITAN_OUTPUT_FORMAT`. It currently applies to
|
|
106
|
+
`repos`, `symbols`, and `stitch`; free-text commands such as `index` and hook
|
|
107
|
+
commands keep their existing output.
|
|
108
|
+
|
|
63
109
|
## Cross-repo context bridge (Layer 2.5)
|
|
64
110
|
|
|
65
111
|
The per-repo graph stops at a repo boundary, but service-oriented architectures
|
|
@@ -256,11 +302,12 @@ witan's). Run `witan-code setup` on its own for a witan-code-only install, or
|
|
|
256
302
|
when witan-code isn't importable from witan's environment. See
|
|
257
303
|
[Hooks](#hooks) and [Skill](#skill).
|
|
258
304
|
|
|
259
|
-
This downloads the omnigraph release pinned by `_OMNIGRAPH_VERSION` in
|
|
260
|
-
|
|
261
|
-
`
|
|
262
|
-
`
|
|
263
|
-
|
|
305
|
+
This downloads the omnigraph release pinned by `_OMNIGRAPH_VERSION` in the
|
|
306
|
+
shared installer
|
|
307
|
+
[`witan_core.omnigraph_install`](../../../packages/witan-core/witan_core/omnigraph_install.py)
|
|
308
|
+
— the same pin `witan` uses (both import it from `witan-core`), bumped by
|
|
309
|
+
Renovate (see the repo-root `renovate.json`'s `omnigraph-version`
|
|
310
|
+
customManager). There is no build-time bundling of the binary into the wheel —
|
|
264
311
|
`witan-code setup` (or `witan setup`) is the only source of the binary, and
|
|
265
312
|
re-running it is how you pick up a version bump.
|
|
266
313
|
|
|
@@ -288,12 +335,35 @@ symlink alternative:
|
|
|
288
335
|
| `WITAN_AUTHOR` / `USER` | `unknown` | attribution string |
|
|
289
336
|
| `WITAN_REPO` | — | override the detected repo slug |
|
|
290
337
|
| `WITAN_CODE_OPTIMIZE_INTERVAL` | `86400` (daily) | throttle window (seconds) for `checkpoint`'s opportunistic store compaction; `0` disables it |
|
|
338
|
+
| `WITAN_CONFIG` | `~/.config/witan/config.toml` | config file path (see below) |
|
|
339
|
+
| `WITAN_TARGET` | — | force a named `[targets.<name>]` block instead of auto-detecting one |
|
|
291
340
|
|
|
292
341
|
The store URI for a repo is `<dir>/<sanitized-slug>.omni`, where the slug's `/`
|
|
293
342
|
and `:` are replaced with `_`. The shared cross-repo bridge lives alongside them
|
|
294
343
|
at `<dir>/_bridge.omni` and is created lazily on the first index that yields any
|
|
295
344
|
bindings.
|
|
296
345
|
|
|
346
|
+
### config.toml
|
|
347
|
+
|
|
348
|
+
witan-code reads the same `config.toml` as witan (witan-council) — a global
|
|
349
|
+
`code_dir`/`author`, plus named `[targets.<name>]` blocks that override them
|
|
350
|
+
and are selected by `WITAN_TARGET`, `load(target=...)` (Python API only — no
|
|
351
|
+
CLI `--target` flag yet), or auto-detection against the current
|
|
352
|
+
repo/checkout (`match_paths` > `match_repos` > `match_hosts` > `match_orgs`
|
|
353
|
+
— see witan's README/`witan/config.py` docstring for the full precedence).
|
|
354
|
+
Because the file is shared, one target block can carry witan's
|
|
355
|
+
`server`/`graph`/`token` alongside `code_dir` under
|
|
356
|
+
the same name — each server reads only the fields it knows:
|
|
357
|
+
|
|
358
|
+
```toml
|
|
359
|
+
[targets.work]
|
|
360
|
+
server = "http://witan.internal:8080" # witan (witan-council)
|
|
361
|
+
code_dir = "/mnt/work/witan-code" # witan-code
|
|
362
|
+
match_orgs = ["myorg"]
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Environment variables always win over `config.toml`.
|
|
366
|
+
|
|
297
367
|
## MCP tools
|
|
298
368
|
|
|
299
369
|
Each tool resolves the per-repo store from the current working directory and
|
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: witan-code
|
|
3
|
-
Version: 0.3.0
|
|
4
|
-
Summary: witan-code — tree-sitter code graph + cross-repo bridge (mounts under `witan code`)
|
|
5
|
-
Project-URL: Homepage, https://github.com/mitodl/agent-kit/tree/main/mcp/servers/witan-code
|
|
6
|
-
Project-URL: Repository, https://github.com/mitodl/agent-kit
|
|
7
|
-
Project-URL: Documentation, https://github.com/mitodl/agent-kit/blob/main/mcp/servers/witan-code/README.md
|
|
8
|
-
Project-URL: Issues, https://github.com/mitodl/agent-kit/issues
|
|
9
|
-
License-Expression: BSD-3-Clause
|
|
10
|
-
Keywords: agent,code-graph,coding-agent,mcp,static-analysis,tree-sitter
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Requires-Python: >=3.11
|
|
13
|
-
Requires-Dist: agent-config-kit<1,>=0.4
|
|
14
|
-
Requires-Dist: cyclopts<5,>=4
|
|
15
|
-
Requires-Dist: fastmcp<4,>=3.4.2
|
|
16
|
-
Requires-Dist: pyyaml<7,>=6
|
|
17
|
-
Requires-Dist: rich>=13
|
|
18
|
-
Requires-Dist: tree-sitter-bash<0.26,>=0.25
|
|
19
|
-
Requires-Dist: tree-sitter-go<0.26,>=0.25
|
|
20
|
-
Requires-Dist: tree-sitter-hcl<1.3,>=1.2
|
|
21
|
-
Requires-Dist: tree-sitter-python<0.26,>=0.25
|
|
22
|
-
Requires-Dist: tree-sitter-sql<0.4,>=0.3
|
|
23
|
-
Requires-Dist: tree-sitter-typescript<0.24,>=0.23
|
|
24
|
-
Requires-Dist: tree-sitter-yaml<0.8,>=0.7
|
|
25
|
-
Requires-Dist: tree-sitter<0.26,>=0.25
|
|
26
|
-
Description-Content-Type: text/markdown
|
|
27
|
-
|
|
28
1
|
# witan-code
|
|
29
2
|
|
|
30
3
|
> "witan" is pronounced `WIT-ən` (/ˈwɪtən/) — rhymes with "written" minus the r.
|
|
@@ -87,6 +60,22 @@ machine's local code index.
|
|
|
87
60
|
- [CLI reference](https://github.com/mitodl/agent-kit/blob/main/mcp/servers/witan-code/docs/CLI_REFERENCE.md) — every `witan-code` command with
|
|
88
61
|
its full flag table and an example invocation.
|
|
89
62
|
|
|
63
|
+
## CLI structured output
|
|
64
|
+
|
|
65
|
+
`witan-code` table-producing commands can emit machine-readable output instead
|
|
66
|
+
of Rich tables:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
witan-code --output-format json repos
|
|
70
|
+
witan-code --output-format yaml symbols --role exported
|
|
71
|
+
witan --output-format toml code stitch --unresolved
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Supported formats are `txt` (default), `json`, `toml`, and `yaml`. The same
|
|
75
|
+
setting is available through `WITAN_OUTPUT_FORMAT`. It currently applies to
|
|
76
|
+
`repos`, `symbols`, and `stitch`; free-text commands such as `index` and hook
|
|
77
|
+
commands keep their existing output.
|
|
78
|
+
|
|
90
79
|
## Cross-repo context bridge (Layer 2.5)
|
|
91
80
|
|
|
92
81
|
The per-repo graph stops at a repo boundary, but service-oriented architectures
|
|
@@ -283,11 +272,12 @@ witan's). Run `witan-code setup` on its own for a witan-code-only install, or
|
|
|
283
272
|
when witan-code isn't importable from witan's environment. See
|
|
284
273
|
[Hooks](#hooks) and [Skill](#skill).
|
|
285
274
|
|
|
286
|
-
This downloads the omnigraph release pinned by `_OMNIGRAPH_VERSION` in
|
|
287
|
-
|
|
288
|
-
`
|
|
289
|
-
`
|
|
290
|
-
|
|
275
|
+
This downloads the omnigraph release pinned by `_OMNIGRAPH_VERSION` in the
|
|
276
|
+
shared installer
|
|
277
|
+
[`witan_core.omnigraph_install`](../../../packages/witan-core/witan_core/omnigraph_install.py)
|
|
278
|
+
— the same pin `witan` uses (both import it from `witan-core`), bumped by
|
|
279
|
+
Renovate (see the repo-root `renovate.json`'s `omnigraph-version`
|
|
280
|
+
customManager). There is no build-time bundling of the binary into the wheel —
|
|
291
281
|
`witan-code setup` (or `witan setup`) is the only source of the binary, and
|
|
292
282
|
re-running it is how you pick up a version bump.
|
|
293
283
|
|
|
@@ -315,12 +305,35 @@ symlink alternative:
|
|
|
315
305
|
| `WITAN_AUTHOR` / `USER` | `unknown` | attribution string |
|
|
316
306
|
| `WITAN_REPO` | — | override the detected repo slug |
|
|
317
307
|
| `WITAN_CODE_OPTIMIZE_INTERVAL` | `86400` (daily) | throttle window (seconds) for `checkpoint`'s opportunistic store compaction; `0` disables it |
|
|
308
|
+
| `WITAN_CONFIG` | `~/.config/witan/config.toml` | config file path (see below) |
|
|
309
|
+
| `WITAN_TARGET` | — | force a named `[targets.<name>]` block instead of auto-detecting one |
|
|
318
310
|
|
|
319
311
|
The store URI for a repo is `<dir>/<sanitized-slug>.omni`, where the slug's `/`
|
|
320
312
|
and `:` are replaced with `_`. The shared cross-repo bridge lives alongside them
|
|
321
313
|
at `<dir>/_bridge.omni` and is created lazily on the first index that yields any
|
|
322
314
|
bindings.
|
|
323
315
|
|
|
316
|
+
### config.toml
|
|
317
|
+
|
|
318
|
+
witan-code reads the same `config.toml` as witan (witan-council) — a global
|
|
319
|
+
`code_dir`/`author`, plus named `[targets.<name>]` blocks that override them
|
|
320
|
+
and are selected by `WITAN_TARGET`, `load(target=...)` (Python API only — no
|
|
321
|
+
CLI `--target` flag yet), or auto-detection against the current
|
|
322
|
+
repo/checkout (`match_paths` > `match_repos` > `match_hosts` > `match_orgs`
|
|
323
|
+
— see witan's README/`witan/config.py` docstring for the full precedence).
|
|
324
|
+
Because the file is shared, one target block can carry witan's
|
|
325
|
+
`server`/`graph`/`token` alongside `code_dir` under
|
|
326
|
+
the same name — each server reads only the fields it knows:
|
|
327
|
+
|
|
328
|
+
```toml
|
|
329
|
+
[targets.work]
|
|
330
|
+
server = "http://witan.internal:8080" # witan (witan-council)
|
|
331
|
+
code_dir = "/mnt/work/witan-code" # witan-code
|
|
332
|
+
match_orgs = ["myorg"]
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Environment variables always win over `config.toml`.
|
|
336
|
+
|
|
324
337
|
## MCP tools
|
|
325
338
|
|
|
326
339
|
Each tool resolves the per-repo store from the current working directory and
|
|
@@ -7,7 +7,13 @@ the `witan` umbrella package. Run `witan-code --help` or `witan-code
|
|
|
7
7
|
<command> --help` for the live version of this table — it is generated from
|
|
8
8
|
the same docstrings.
|
|
9
9
|
|
|
10
|
-
Global flags: `--help` / `-h`, `--version`.
|
|
10
|
+
Global flags: `--help` / `-h`, `--version`, `--output-format txt|json|toml|yaml`.
|
|
11
|
+
|
|
12
|
+
`--output-format` controls table-producing commands (`repos`, `symbols`, and
|
|
13
|
+
`stitch`) and is also available as `WITAN_OUTPUT_FORMAT`. The default `txt`
|
|
14
|
+
renders the existing Rich tables; `json`, `toml`, and `yaml` emit a wrapper
|
|
15
|
+
object with `title` and `rows` for machine parsing. The same option works when
|
|
16
|
+
mounted under the umbrella CLI, e.g. `witan --output-format json code repos`.
|
|
11
17
|
|
|
12
18
|
Boolean flags follow cyclopts' `--flag`/`--no-flag` convention; both forms are
|
|
13
19
|
always available even where only `--flag` is shown below.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "witan-code"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "witan-code — tree-sitter code graph + cross-repo bridge (mounts under `witan code`)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "BSD-3-Clause"
|
|
@@ -11,8 +11,12 @@ classifiers = [
|
|
|
11
11
|
]
|
|
12
12
|
dependencies = [
|
|
13
13
|
"agent-config-kit>=0.4,<1",
|
|
14
|
+
"witan-core[cli]>=0.2,<1",
|
|
14
15
|
"fastmcp>=3.4.2,<4",
|
|
15
16
|
"cyclopts>=4,<5",
|
|
17
|
+
# [targets.<name>] override models (config.py) — also transitively pulled
|
|
18
|
+
# in by fastmcp, declared explicitly since config.py imports it directly.
|
|
19
|
+
"pydantic>=2,<3",
|
|
16
20
|
# Pinned to 0.25.x: tree-sitter 0.26.0 has a use-after-free in its pyo3 Node
|
|
17
21
|
# binding that segfaults (exit 139) under the heavy .parent()/.children()
|
|
18
22
|
# churn _walk_defs does on real-world-sized files — deterministically on the
|
|
@@ -35,6 +39,7 @@ dependencies = [
|
|
|
35
39
|
"tree-sitter-hcl>=1.2,<1.3",
|
|
36
40
|
"tree-sitter-go>=0.25,<0.26",
|
|
37
41
|
"pyyaml>=6,<7",
|
|
42
|
+
"tomli-w>=1,<2",
|
|
38
43
|
"rich>=13",
|
|
39
44
|
]
|
|
40
45
|
|
|
@@ -56,7 +61,8 @@ build-backend = "hatchling.build"
|
|
|
56
61
|
# agent-config-kit version bumped alongside it — even before that version
|
|
57
62
|
# has actually landed on PyPI. Published wheels only see the PyPI-range
|
|
58
63
|
# dependency declared above.
|
|
59
|
-
agent-config-kit = {
|
|
64
|
+
agent-config-kit = { workspace = true }
|
|
65
|
+
witan-core = { workspace = true }
|
|
60
66
|
|
|
61
67
|
[dependency-groups]
|
|
62
68
|
test = ["pytest>=8"]
|
|
@@ -69,7 +75,7 @@ testpaths = ["tests"]
|
|
|
69
75
|
packages = ["witan_code"]
|
|
70
76
|
|
|
71
77
|
[tool.bumpversion]
|
|
72
|
-
current_version = "0.
|
|
78
|
+
current_version = "0.6.0"
|
|
73
79
|
allow_dirty = true
|
|
74
80
|
|
|
75
81
|
[[tool.bumpversion.files]]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Tests for the global ``--output-format`` option."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from witan_code import cli as cli_module
|
|
10
|
+
from witan_code import output as output_module
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture(autouse=True)
|
|
14
|
+
def _reset_output_format():
|
|
15
|
+
"""Every test starts and ends on the txt default regardless of order."""
|
|
16
|
+
output_module.set_output_format("txt")
|
|
17
|
+
yield
|
|
18
|
+
output_module.set_output_format("txt")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_render_table_json_dumps_normalized_rows(capsys):
|
|
22
|
+
output_module.set_output_format("json")
|
|
23
|
+
|
|
24
|
+
cli_module._render_table(
|
|
25
|
+
title="Indexed repositories",
|
|
26
|
+
columns=["repo", "files"],
|
|
27
|
+
rows=[{"repo": "https://github.com/test/repo", "files": None}],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
payload = json.loads(capsys.readouterr().out)
|
|
31
|
+
assert payload == {
|
|
32
|
+
"title": "Indexed repositories",
|
|
33
|
+
"rows": [{"repo": "https://github.com/test/repo", "files": ""}],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_render_table_toml_dumps_normalized_rows(capsys):
|
|
38
|
+
import tomllib
|
|
39
|
+
|
|
40
|
+
output_module.set_output_format("toml")
|
|
41
|
+
|
|
42
|
+
cli_module._render_table(
|
|
43
|
+
title="Symbol table — https://github.com/test/repo",
|
|
44
|
+
columns=["role", "refs"],
|
|
45
|
+
rows=[{"role": "exported", "refs": 2}],
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
payload = tomllib.loads(capsys.readouterr().out)
|
|
49
|
+
assert payload["title"] == "Symbol table — https://github.com/test/repo"
|
|
50
|
+
assert payload["rows"] == [{"role": "exported", "refs": 2}]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_repos_honors_structured_output(tmp_path, monkeypatch, capsys):
|
|
54
|
+
code_dir = tmp_path / "code"
|
|
55
|
+
code_dir.mkdir()
|
|
56
|
+
store = code_dir / "https_github.com_test_repo.omni"
|
|
57
|
+
store.mkdir()
|
|
58
|
+
(code_dir / f"{store.name}.repo").write_text("https://github.com/test/repo")
|
|
59
|
+
monkeypatch.setenv("WITAN_CODE_DIR", str(code_dir))
|
|
60
|
+
monkeypatch.setattr(
|
|
61
|
+
cli_module,
|
|
62
|
+
"_code_store_stats",
|
|
63
|
+
lambda store: ("https://github.com/test/repo", "7"),
|
|
64
|
+
)
|
|
65
|
+
monkeypatch.setattr(cli_module, "_dir_stats", lambda path: (1024, "2026-07-13"))
|
|
66
|
+
output_module.set_output_format("json")
|
|
67
|
+
|
|
68
|
+
cli_module.repos()
|
|
69
|
+
|
|
70
|
+
payload = json.loads(capsys.readouterr().out)
|
|
71
|
+
assert payload == {
|
|
72
|
+
"title": "Indexed repositories",
|
|
73
|
+
"rows": [
|
|
74
|
+
{
|
|
75
|
+
"repo": "https://github.com/test/repo",
|
|
76
|
+
"files": "7",
|
|
77
|
+
"size": "1.0KB",
|
|
78
|
+
"last indexed": "2026-07-13",
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_launcher_sets_output_format_and_forwards_tokens(monkeypatch):
|
|
85
|
+
calls = []
|
|
86
|
+
monkeypatch.setattr(cli_module, "app", lambda tokens: calls.append(tokens))
|
|
87
|
+
|
|
88
|
+
cli_module._launcher("repos", output_format="yaml")
|
|
89
|
+
|
|
90
|
+
assert output_module.get_output_format() == "yaml"
|
|
91
|
+
assert calls == [("repos",)]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Canonical cluster graph-id derivation (config.graph_id).
|
|
2
|
+
|
|
3
|
+
This id is a cross-repo contract: witan-code addresses `--graph <id>` and
|
|
4
|
+
ol-infrastructure's cluster.yaml provisioning declares the same `<id>`. If this
|
|
5
|
+
algorithm drifts from the provisioning side, clients address graphs the cluster
|
|
6
|
+
never created — so pin the observable mapping here.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from witan_code import config
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_graph_id_canonical_example():
|
|
13
|
+
# The example from the deployment decision; must be byte-for-byte stable.
|
|
14
|
+
assert (
|
|
15
|
+
config.graph_id("https://github.com/mitodl/ol-django")
|
|
16
|
+
== "code-github-com-mitodl-ol-django"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_graph_id_matches_constraint():
|
|
21
|
+
for repo in (
|
|
22
|
+
"https://github.com/mitodl/ol-django",
|
|
23
|
+
"http://example.com/x",
|
|
24
|
+
"git@github.com:mitodl/agent-kit.git",
|
|
25
|
+
"github.com/mitodl/ol-infrastructure",
|
|
26
|
+
"https://github.com/MITODL/Mixed_Case.Repo",
|
|
27
|
+
):
|
|
28
|
+
assert config.GRAPH_ID_RE.match(config.graph_id(repo)), repo
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_graph_id_no_underscores_and_lowercase():
|
|
32
|
+
gid = config.graph_id("https://github.com/MITODL/Mixed_Case.Repo")
|
|
33
|
+
assert "_" not in gid
|
|
34
|
+
assert gid == gid.lower()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_graph_id_strips_scheme():
|
|
38
|
+
with_scheme = config.graph_id("https://github.com/mitodl/x")
|
|
39
|
+
without = config.graph_id("github.com/mitodl/x")
|
|
40
|
+
assert with_scheme == without == "code-github-com-mitodl-x"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_graph_id_is_prefixed():
|
|
44
|
+
assert config.graph_id("github.com/a/b").startswith(config.CODE_GRAPH_PREFIX)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_graph_id_long_repo_truncates_with_hash():
|
|
48
|
+
long_repo = "https://github.com/mitodl/" + "a" * 200
|
|
49
|
+
gid = config.graph_id(long_repo)
|
|
50
|
+
assert len(gid) <= 64
|
|
51
|
+
assert config.GRAPH_ID_RE.match(gid)
|
|
52
|
+
# Distinct long repos disambiguate via the URI hash, never colliding.
|
|
53
|
+
other = config.graph_id("https://github.com/mitodl/" + "a" * 199 + "b")
|
|
54
|
+
assert gid != other
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_graph_id_is_deterministic():
|
|
58
|
+
repo = "https://github.com/mitodl/" + "z" * 100
|
|
59
|
+
assert config.graph_id(repo) == config.graph_id(repo)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_bridge_graph_id_constant():
|
|
63
|
+
assert config.BRIDGE_GRAPH_ID == "code-bridge"
|
|
64
|
+
assert config.GRAPH_ID_RE.match(config.BRIDGE_GRAPH_ID)
|