agent-codinglanguage-mapper 1.0.0__py3-none-any.whl
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.
- agent_codinglanguage_mapper/__init__.py +20 -0
- agent_codinglanguage_mapper/__main__.py +4 -0
- agent_codinglanguage_mapper/_version.py +2 -0
- agent_codinglanguage_mapper/adapters/__init__.py +4 -0
- agent_codinglanguage_mapper/adapters/delphi.py +822 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/__init__.py +6 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/comment_builder.py +29 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/consts.py +345 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/grammar.py +460 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/lark_builder.py +2674 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/lark_tokens.py +237 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/lsp_server.py +1832 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/nodes.py +371 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/parser.py +193 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/preprocessor.py +997 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/project_discovery.py +518 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/project_indexer.py +319 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/semantic.py +375 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/semantic_builder.py +1384 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/source_reader.py +17 -0
- agent_codinglanguage_mapper/adapters/delphi_frontend/workspace.py +67 -0
- agent_codinglanguage_mapper/adapters/tree_sitter.py +1722 -0
- agent_codinglanguage_mapper/cli.py +138 -0
- agent_codinglanguage_mapper/discovery.py +1328 -0
- agent_codinglanguage_mapper/indexer.py +1102 -0
- agent_codinglanguage_mapper/integration.py +186 -0
- agent_codinglanguage_mapper/lsp_server.py +413 -0
- agent_codinglanguage_mapper/lsp_service.py +447 -0
- agent_codinglanguage_mapper/mapper.py +596 -0
- agent_codinglanguage_mapper/models.py +101 -0
- agent_codinglanguage_mapper/protocol.py +152 -0
- agent_codinglanguage_mapper/rendering.py +153 -0
- agent_codinglanguage_mapper/templates/opencode/plugins/codebase_map.ts +191 -0
- agent_codinglanguage_mapper/templates/skill/SKILL.md +52 -0
- agent_codinglanguage_mapper/worker.py +29 -0
- agent_codinglanguage_mapper/workspace.py +114 -0
- agent_codinglanguage_mapper-1.0.0.dist-info/METADATA +383 -0
- agent_codinglanguage_mapper-1.0.0.dist-info/RECORD +42 -0
- agent_codinglanguage_mapper-1.0.0.dist-info/WHEEL +5 -0
- agent_codinglanguage_mapper-1.0.0.dist-info/entry_points.txt +2 -0
- agent_codinglanguage_mapper-1.0.0.dist-info/licenses/LICENSE +373 -0
- agent_codinglanguage_mapper-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import dataclass, field, fields, is_dataclass
|
|
6
|
+
from types import MappingProxyType
|
|
7
|
+
from typing import Any, Callable, Mapping, TypeVar, cast
|
|
8
|
+
|
|
9
|
+
from .models import Language, Problem, Project, Reference, Relation, SourceFile, Symbol
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def stable_id(kind: str, *parts: str) -> str:
|
|
13
|
+
material = "\x1f".join((kind, *parts)).encode("utf-8")
|
|
14
|
+
return f"{kind}:{hashlib.sha256(material).hexdigest()[:20]}"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _record(value: object) -> object:
|
|
18
|
+
if hasattr(value, "value"):
|
|
19
|
+
return getattr(value, "value")
|
|
20
|
+
if isinstance(value, tuple):
|
|
21
|
+
return [_record(item) for item in value]
|
|
22
|
+
if isinstance(value, dict):
|
|
23
|
+
return {key: _record(item) for key, item in sorted(value.items())}
|
|
24
|
+
if is_dataclass(value) and not isinstance(value, type):
|
|
25
|
+
instance = cast(Any, value)
|
|
26
|
+
return {item.name: _record(getattr(instance, item.name)) for item in fields(instance)}
|
|
27
|
+
return value
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
_T = TypeVar("_T")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _group(items: tuple[_T, ...], key: Callable[[_T], str]) -> dict[str, tuple[_T, ...]]:
|
|
34
|
+
grouped: dict[str, list[_T]] = {}
|
|
35
|
+
for item in items:
|
|
36
|
+
grouped.setdefault(key(item), []).append(item)
|
|
37
|
+
return {key: tuple(value) for key, value in grouped.items()}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class WorkspaceSnapshot:
|
|
42
|
+
projects: tuple[Project, ...] = ()
|
|
43
|
+
files: tuple[SourceFile, ...] = ()
|
|
44
|
+
symbols: tuple[Symbol, ...] = ()
|
|
45
|
+
references: tuple[Reference, ...] = ()
|
|
46
|
+
relations: tuple[Relation, ...] = ()
|
|
47
|
+
problems: tuple[Problem, ...] = ()
|
|
48
|
+
source_digests: tuple[tuple[str, str], ...] = ()
|
|
49
|
+
metadata_digests: tuple[tuple[str, str], ...] = ()
|
|
50
|
+
revision: str = field(init=False)
|
|
51
|
+
projects_by_id: Mapping[str, Project] = field(init=False, repr=False)
|
|
52
|
+
files_by_id: Mapping[str, SourceFile] = field(init=False, repr=False)
|
|
53
|
+
symbols_by_id: Mapping[str, Symbol] = field(init=False, repr=False)
|
|
54
|
+
symbols_by_file: Mapping[str, tuple[Symbol, ...]] = field(init=False, repr=False)
|
|
55
|
+
symbol_aliases_by_id: Mapping[str, tuple[Symbol, ...]] = field(init=False, repr=False)
|
|
56
|
+
relations_by_source: Mapping[str, tuple[Relation, ...]] = field(init=False, repr=False)
|
|
57
|
+
relations_by_target: Mapping[str, tuple[Relation, ...]] = field(init=False, repr=False)
|
|
58
|
+
|
|
59
|
+
def __post_init__(self) -> None:
|
|
60
|
+
names = (
|
|
61
|
+
"projects",
|
|
62
|
+
"files",
|
|
63
|
+
"symbols",
|
|
64
|
+
"references",
|
|
65
|
+
"relations",
|
|
66
|
+
"problems",
|
|
67
|
+
"source_digests",
|
|
68
|
+
"metadata_digests",
|
|
69
|
+
)
|
|
70
|
+
for name in names:
|
|
71
|
+
object.__setattr__(self, name, tuple(getattr(self, name)))
|
|
72
|
+
revision_names = ("projects", "files", "source_digests", "metadata_digests")
|
|
73
|
+
payload = {name: _record(getattr(self, name)) for name in revision_names}
|
|
74
|
+
encoded = json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8")
|
|
75
|
+
object.__setattr__(self, "revision", hashlib.sha256(encoded).hexdigest())
|
|
76
|
+
object.__setattr__(self, "projects_by_id", MappingProxyType({item.project_id: item for item in self.projects}))
|
|
77
|
+
object.__setattr__(self, "files_by_id", MappingProxyType({item.file_id: item for item in self.files}))
|
|
78
|
+
object.__setattr__(self, "symbols_by_id", MappingProxyType({item.target_id: item for item in self.symbols}))
|
|
79
|
+
object.__setattr__(self, "symbols_by_file", MappingProxyType(_group(self.symbols, lambda item: item.file_id)))
|
|
80
|
+
alias_groups: dict[tuple[object, ...], list[Symbol]] = {}
|
|
81
|
+
for symbol in self.symbols:
|
|
82
|
+
alias_groups.setdefault(_symbol_alias_key(symbol), []).append(symbol)
|
|
83
|
+
aliases_by_id = {
|
|
84
|
+
symbol.target_id: tuple(group)
|
|
85
|
+
for group in alias_groups.values()
|
|
86
|
+
for symbol in group
|
|
87
|
+
}
|
|
88
|
+
object.__setattr__(self, "symbol_aliases_by_id", MappingProxyType(aliases_by_id))
|
|
89
|
+
object.__setattr__(
|
|
90
|
+
self,
|
|
91
|
+
"relations_by_source",
|
|
92
|
+
MappingProxyType(_group(self.relations, lambda item: item.source_id)),
|
|
93
|
+
)
|
|
94
|
+
object.__setattr__(
|
|
95
|
+
self,
|
|
96
|
+
"relations_by_target",
|
|
97
|
+
MappingProxyType(_group(self.relations, lambda item: item.target_id)),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _symbol_alias_key(symbol: Symbol) -> tuple[object, ...]:
|
|
102
|
+
if symbol.language is not Language.DELPHI:
|
|
103
|
+
return (symbol.target_id,)
|
|
104
|
+
anchor = symbol.name_range or symbol.range
|
|
105
|
+
return (
|
|
106
|
+
symbol.project_id,
|
|
107
|
+
symbol.language,
|
|
108
|
+
symbol.kind,
|
|
109
|
+
symbol.role,
|
|
110
|
+
symbol.name.casefold(),
|
|
111
|
+
symbol.qualified_name.casefold(),
|
|
112
|
+
" ".join(symbol.signature.casefold().split()),
|
|
113
|
+
anchor,
|
|
114
|
+
)
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-codinglanguage-mapper
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Agent-focused semantic mapping and language services for mixed-language codebases.
|
|
5
|
+
Author: Dark Light
|
|
6
|
+
License-Expression: MPL-2.0
|
|
7
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Operating System :: MacOS
|
|
11
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Software Development :: Compilers
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: <3.15,>=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: lark<2,>=1.1.9
|
|
25
|
+
Requires-Dist: lsprotocol>=2023.0.1
|
|
26
|
+
Requires-Dist: pathspec<1,>=0.12
|
|
27
|
+
Requires-Dist: pygls<2,>=1.3.0
|
|
28
|
+
Requires-Dist: tomli>=2; python_version < "3.11"
|
|
29
|
+
Requires-Dist: tree-sitter==0.26.0
|
|
30
|
+
Requires-Dist: tree-sitter-python==0.25.0
|
|
31
|
+
Requires-Dist: tree-sitter-rust==0.24.2
|
|
32
|
+
Requires-Dist: tree-sitter-c-sharp==0.23.5
|
|
33
|
+
Requires-Dist: tree-sitter-cpp==0.23.4
|
|
34
|
+
Provides-Extra: test
|
|
35
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
36
|
+
Requires-Dist: pytest-cov>=5; extra == "test"
|
|
37
|
+
Requires-Dist: tomli>=2; python_version < "3.11" and extra == "test"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
40
|
+
Requires-Dist: mypy==1.19.1; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
43
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
44
|
+
Requires-Dist: tomli>=2; extra == "dev"
|
|
45
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
# Agent Coding Language Mapper
|
|
49
|
+
|
|
50
|
+
`agent-codinglanguage-mapper` gives coding agents a bounded semantic view of mixed-language repositories. It maps Python, Rust, C#, Delphi, and C++ into one language-neutral index and exposes that index through a Python API, a command-line interface, Protocol 3, an LSP server, and an OpenCode skill/plugin pair.
|
|
51
|
+
|
|
52
|
+
The mapper is designed for code investigation without sending whole files through `grep`, `cat`, or generic file-reading tools. Responses are paginated, carry stable target IDs and source citations, distinguish proven from partial relations, and preserve focus across follow-up requests.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python -m pip install agent-codinglanguage-mapper
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Python 3.10 through 3.14 is supported on Windows, macOS, and Linux.
|
|
61
|
+
|
|
62
|
+
Check the installation against a repository:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
codinglanguage-mapper doctor --root .
|
|
66
|
+
codinglanguage-mapper view --root . --layer overview --format markdown
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Python API
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from agent_codinglanguage_mapper import AgentRequest, CodebaseMapper, Language
|
|
73
|
+
|
|
74
|
+
with CodebaseMapper.open(".", languages="auto") as mapper:
|
|
75
|
+
found = mapper.request(
|
|
76
|
+
AgentRequest(
|
|
77
|
+
action="find",
|
|
78
|
+
query="PaymentService",
|
|
79
|
+
languages=(Language.PYTHON, Language.CSHARP),
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
item = found.result["items"][0]
|
|
84
|
+
detail = mapper.request(
|
|
85
|
+
AgentRequest(
|
|
86
|
+
action="inspect",
|
|
87
|
+
target_id=item["target_id"],
|
|
88
|
+
detail="body",
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
print(detail.to_mapping())
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The public types are:
|
|
95
|
+
|
|
96
|
+
- `CodebaseMapper`
|
|
97
|
+
- `AgentRequest` and `AgentResponse`
|
|
98
|
+
- `Language`
|
|
99
|
+
- `Project` and `SourceFile`
|
|
100
|
+
- `Symbol`, `Reference`, and `Relation`
|
|
101
|
+
- `Problem` and `SourceRange`
|
|
102
|
+
|
|
103
|
+
Parser-native nodes are intentionally private. Ranges are zero-based and half-open. Rendered citations use one-based line numbers, for example `src/payment.py:42`.
|
|
104
|
+
|
|
105
|
+
`CodebaseMapper.open()` creates an outline index first. `inspect`, `focus`, and outgoing `callees`/`uses` traces load detail only for the selected source file. Incoming `references`/`callers`/`used_by` traces load the remaining workspace detail because their evidence can originate in any indexed file. Parsed detail and focus remain resident for the mapper session. The same optimized path is used for small and large files.
|
|
106
|
+
|
|
107
|
+
The private session index retains each parser-native outline tree, its source buffer, a source digest, and a lightweight file fingerprint. A detail upgrade therefore queries the existing tree instead of rereading or reparsing every indexed source. When an indexed source changes during a worker session, the mapper rebuilds the outline snapshot, updates `workspace_revision`, clears stale detail/focus state, and invalidates old cursors before answering the next request. Parser-native nodes never appear in the public result types.
|
|
108
|
+
|
|
109
|
+
## CLI
|
|
110
|
+
|
|
111
|
+
The installed command is `codinglanguage-mapper`.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Layered views
|
|
115
|
+
codinglanguage-mapper view --root . --layer projects --format markdown
|
|
116
|
+
codinglanguage-mapper view --root . --layer files --language rust --format json
|
|
117
|
+
codinglanguage-mapper view --root . --layer symbols --query PaymentService
|
|
118
|
+
codinglanguage-mapper view --root . --layer implementation --query PaymentService
|
|
119
|
+
codinglanguage-mapper view --root . --layer references --query PaymentService
|
|
120
|
+
|
|
121
|
+
# Persistent JSON index
|
|
122
|
+
codinglanguage-mapper index --root . --out .mapper/index.json
|
|
123
|
+
|
|
124
|
+
# Discovery and parser diagnostics
|
|
125
|
+
codinglanguage-mapper doctor --root . --format json
|
|
126
|
+
|
|
127
|
+
# Language Server Protocol over stdio
|
|
128
|
+
codinglanguage-mapper lsp --root .
|
|
129
|
+
|
|
130
|
+
# Protocol 3 NDJSON worker
|
|
131
|
+
codinglanguage-mapper worker --root .
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Available view layers are `overview`, `projects`, `languages`, `files`, `file`, `symbols`, `symbol`, `implementation`, `references`, and `problems`.
|
|
135
|
+
|
|
136
|
+
## Protocol 3
|
|
137
|
+
|
|
138
|
+
Protocol 3 is the bounded request/response contract used by the worker and OpenCode plugin.
|
|
139
|
+
|
|
140
|
+
Actions:
|
|
141
|
+
|
|
142
|
+
| Action | Purpose |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `open` | Return projects and establish the workspace revision. |
|
|
145
|
+
| `find` | Search indexed symbols with optional project, file, and language filters. |
|
|
146
|
+
| `inspect` | Return one selected detail layer for a target. |
|
|
147
|
+
| `trace` | Follow one explicit relation from a target. |
|
|
148
|
+
| `focus` | Select a target while preserving project/file/language focus. |
|
|
149
|
+
| `problems` | Return structured discovery, syntax, and semantic problems. |
|
|
150
|
+
|
|
151
|
+
Details are `summary`, `declaration`, `members`, `context`, `body`, and `implementations`. Relations are `references`, `callers`, `callees`, `uses`, `used_by`, `inherits`, `implements`, and `ffi`.
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"action": "trace",
|
|
156
|
+
"target_id": "symbol:...",
|
|
157
|
+
"relation": "callers",
|
|
158
|
+
"languages": ["csharp", "cpp"],
|
|
159
|
+
"max_items": 12,
|
|
160
|
+
"max_chars": 12000
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Each response contains:
|
|
165
|
+
|
|
166
|
+
- `schema`: always `3`
|
|
167
|
+
- `workspace_revision`: invalidates stale cursors after codebase changes
|
|
168
|
+
- `focus`: current project, file, target, and language selection
|
|
169
|
+
- `result`: bounded result items
|
|
170
|
+
- `page`: returned/total counts, truncation state, and `next_cursor`
|
|
171
|
+
- `context`: character count and approximate token count
|
|
172
|
+
|
|
173
|
+
Symbol items include a one-based `citation` and a `role` value. `role` is `declaration`, `implementation`, or `definition`; clients must use it when the language distinguishes a declaration from its implementation and must copy `citation` verbatim instead of recalculating it from the zero-based ranges.
|
|
174
|
+
|
|
175
|
+
Relation items include `target_name` and, for statically identifiable native imports, `target_library`. Parser-native nodes and recovery artifacts are never exposed through Protocol 3.
|
|
176
|
+
|
|
177
|
+
After `focus`, an unfiltered `find` inherits the focused project, file, and languages. An explicit `file_id` replaces the focused file. An explicit project or language filter starts a broader scope and clears incompatible narrower inherited filters; include `file_id` explicitly when that narrower restriction is still intended.
|
|
178
|
+
|
|
179
|
+
Defaults are 12 items and 12,000 characters. `max_chars` is capped at 40,000. Source bodies are chunked at 6,000 characters. When `page.truncated` is true, continue with the non-empty `next_cursor`; do not infer omitted items. An individual bounded summary can also contain `"truncated": true` while still exposing its `target_id`, name, language, and citation so the target can be inspected directly.
|
|
180
|
+
|
|
181
|
+
## Resolution Semantics
|
|
182
|
+
|
|
183
|
+
The mapper never fabricates a relation to make a graph look complete.
|
|
184
|
+
|
|
185
|
+
| Resolution | Meaning |
|
|
186
|
+
| --- | --- |
|
|
187
|
+
| `complete` | The relation is proven inside the indexed workspace. |
|
|
188
|
+
| `sound_partial` | Returned evidence is valid, but the available static information may be incomplete. |
|
|
189
|
+
| `ambiguous` | More than one valid target remains. |
|
|
190
|
+
| `unresolved` | No defensible target can be selected. |
|
|
191
|
+
|
|
192
|
+
Same-language cross-file targets become `complete` only when the target is visible in the same project through an explicit Python/Rust import, C/C++ include, Delphi `uses` clause, or the applicable C# namespace rules. A same-name symbol elsewhere in the workspace is not sufficient.
|
|
193
|
+
|
|
194
|
+
Cross-language FFI is recognized only from explicit declarations: Rust `extern`, C/C++ ABI exports, C# `DllImport`/`LibraryImport`, Delphi `external`/`exports`, and statically identifiable Python `ctypes`/`cffi` usage. An import becomes `complete` only when its literal library identity also matches the exporter identity after conventional `lib` prefix and `.dll`/`.dylib`/`.so` suffix normalization. C/C++ exporter identity comes from explicit loadable CMake targets such as `add_library(metrics SHARED metrics.cpp)` or `MODULE` libraries; static libraries, executables, and unspecified library kinds are not treated as runtime FFI outputs. Delphi exporter identity comes from the `library metrics;` declaration. A source filename is never treated as proof of a native output name, and custom or unresolved outputs remain `sound_partial` or `unresolved`.
|
|
195
|
+
|
|
196
|
+
A resolved `ffi` trace includes complete `source_symbol` and `target_symbol` mappings. Their `language`, `role`, and `citation` fields provide both endpoints without a second name-based search.
|
|
197
|
+
|
|
198
|
+
## Project Discovery
|
|
199
|
+
|
|
200
|
+
No environment variable is required for normal discovery. The workspace scanner respects `.gitignore` files at every directory level, applies each pattern relative to its directory, skips standard build/cache directories, and enforces the repository boundary. Directory symlinks are not followed, and workspace-escaping source roots are reported instead of indexed.
|
|
201
|
+
|
|
202
|
+
Supported metadata includes:
|
|
203
|
+
|
|
204
|
+
- Python `pyproject.toml`, `setup.cfg`, and conventional/source layouts
|
|
205
|
+
- Cargo packages and workspaces
|
|
206
|
+
- `.sln` and `.csproj`
|
|
207
|
+
- Delphi `.dpr`, `.dpk`, `.dproj`, and `.groupproj`, including unit search paths, include paths, source paths, project defines, and source declarations; `.dpr` and `.dpk` files are also indexed as source
|
|
208
|
+
- C/C++ `compile_commands.json`, `CMakeLists.txt`, and `.vcxproj`
|
|
209
|
+
|
|
210
|
+
Build scripts are parsed as data and are never executed. Cargo custom library and binary paths are combined rather than treated as alternatives, and solution files resolve their referenced C# project membership. Multiple named project files in one directory keep distinct project identities; matching `.dpr`/`.dproj` pairs share one Delphi project identity. Explicit source membership has priority over broad solution or group metadata. If multiple implicit projects remain equally valid owners, the file is assigned to the synthetic workspace project and an `ambiguous-project-ownership` problem lists the candidate project files. Unresolved variables, missing generated files, missing project references, missing include roots, and paths outside the workspace become structured `Problem` records.
|
|
211
|
+
|
|
212
|
+
## Language Coverage
|
|
213
|
+
|
|
214
|
+
Release-blocking fixtures and LSP assertions cover:
|
|
215
|
+
|
|
216
|
+
| Language | Supported stable range |
|
|
217
|
+
| --- | --- |
|
|
218
|
+
| Python | 2.7 and 3.0 through 3.14 |
|
|
219
|
+
| Rust | Editions 2015, 2018, 2021, and 2024 |
|
|
220
|
+
| C# | C# 1 through C# 14 |
|
|
221
|
+
| Delphi | Delphi 7 through Delphi 13 Florence |
|
|
222
|
+
| C++ | C++98/03 through C++23 |
|
|
223
|
+
|
|
224
|
+
Stable GNU/MSVC C++ extensions and Delphi compiler directives have dedicated fixtures. Preview, Nightly, compiler-internal, and draft syntax is Best-Effort and is reported through explicit compatibility handlers. Unknown stable syntax remains a release-blocking `syntax-error`; parser recovery is never silently accepted.
|
|
225
|
+
|
|
226
|
+
Run the offline formal audit:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
python scripts/audit_language_matrix.py
|
|
230
|
+
pytest -q tests/test_language_matrix.py
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Pinned external evaluations cover CPython, Rust, Roslyn, LLVM, DUnitX, and the official corpora for each pinned Tree-sitter grammar. Populate a missing corpus cache only as an explicit evaluation operation:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
python scripts/evaluate_external_corpora.py --fetch --max-files 20
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Every subsequent run is offline and rejects a dirty or revision-mismatched cache:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
python scripts/evaluate_external_corpora.py \
|
|
243
|
+
--output benchmarks/results/external-corpora.json
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Corpora are evaluation inputs only. They are not vendored and are never accessed at package runtime.
|
|
247
|
+
|
|
248
|
+
## LSP Configuration
|
|
249
|
+
|
|
250
|
+
Start the server with:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
codinglanguage-mapper lsp --root /absolute/path/to/workspace
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
An editor client should launch that command over stdio with the workspace directory as its root. The server advertises diagnostics, definition, hover, references, safe rename, document symbols, workspace symbols, and completion for all five languages.
|
|
257
|
+
|
|
258
|
+
Safe rename requires an exact declaration range and refuses unresolved or ambiguous references. Diagnostics retain the mapper's structured severity and source ranges.
|
|
259
|
+
|
|
260
|
+
Example generic client configuration:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"command": "codinglanguage-mapper",
|
|
265
|
+
"args": ["lsp", "--root", "/absolute/path/to/workspace"],
|
|
266
|
+
"transport": "stdio",
|
|
267
|
+
"filetypes": ["python", "rust", "cs", "pascal", "cpp"]
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## OpenCode Integration
|
|
272
|
+
|
|
273
|
+
Install the packaged skill, plugin, and restricted agent configuration into a repository:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
codinglanguage-mapper opencode install --target . --write
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
This creates:
|
|
280
|
+
|
|
281
|
+
```text
|
|
282
|
+
.agents/skills/mixed-codebase-navigator/SKILL.md
|
|
283
|
+
.opencode/plugins/codebase_map.ts
|
|
284
|
+
opencode.json
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
The generated `mixed-codebase-navigator` agent denies all tools by default and permits only `skill` and `codebase_map`. The plugin maps the active OpenCode directory, serializes Protocol 3 requests through one worker, applies a hard timeout, reports bounded worker `stderr`, restarts after failure, and preserves focus plus warm parser indexes across short idle gaps. It stops the worker when the session is deleted or after 60 seconds without another mapper request.
|
|
288
|
+
|
|
289
|
+
Run it with:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
opencode run \
|
|
293
|
+
--agent mixed-codebase-navigator \
|
|
294
|
+
"Find PaymentService, inspect its implementation, and cite the callers. Use only the installed skill and codebase_map."
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
A typical investigation should:
|
|
298
|
+
|
|
299
|
+
1. Call `codebase_map` with `action: open`.
|
|
300
|
+
2. Use `find` with the narrowest useful language/project/file filters.
|
|
301
|
+
3. Select a returned `target_id` with `focus`.
|
|
302
|
+
4. Request only the required `inspect` detail.
|
|
303
|
+
5. Use `trace` only for explicit Protocol 3 relations.
|
|
304
|
+
6. Follow `next_cursor` when evidence is truncated.
|
|
305
|
+
|
|
306
|
+
## Large-Workspace Performance
|
|
307
|
+
|
|
308
|
+
The release benchmark generates one valid file above 100,000 lines for each language and a mixed workspace above 500,000 lines. On the reference Apple Silicon Mac, release budgets are:
|
|
309
|
+
|
|
310
|
+
- cold parse P95 per 100k file: 3 seconds
|
|
311
|
+
- mixed overview P95: 5 seconds
|
|
312
|
+
- warm search P95: 1.5 seconds
|
|
313
|
+
- inspect and focus P95: 0.5 seconds each
|
|
314
|
+
|
|
315
|
+
Reproduce the benchmark:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
python scripts/benchmark_large_workspaces.py \
|
|
319
|
+
--lines 100000 \
|
|
320
|
+
--samples 5 \
|
|
321
|
+
--output benchmarks/results/macos-current.json
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
The command exits nonzero when any budget is exceeded.
|
|
325
|
+
Private Linux CI records and uploads the same measurements with `--report-only`; the Apple Silicon release budgets are enforced only on the documented reference Mac and are not applied to unrelated hosted hardware.
|
|
326
|
+
|
|
327
|
+
## Offline Ornith/OpenCode Evaluation
|
|
328
|
+
|
|
329
|
+
The repository contains six release E2Es: Python, Rust, C#, Delphi, C++, and a mixed-language FFI workspace. They use the pinned local Ornith revision through vLLM 0.23.0 and OpenCode 1.17.18. The Python scenario continues the same session through a headless OpenCode server and must inspect the focused symbol in a second turn without another `find` or explicit `target_id`, proving that the plugin preserves its warm worker. It then opens an independent session whose target-less `inspect` must be empty, proving that focus never crosses chat boundaries. The harness fails if a model shard is missing, a download is attempted, an unapproved tool is called, any assistant message uses a different agent, model, or provider, a worker survives server shutdown, or golden file/line evidence is absent.
|
|
330
|
+
|
|
331
|
+
Inspect the plan without launching a model:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
python scripts/run_opencode_e2e.py --plan
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Verify the exact local executables, versions, model revision, and weight shards:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
python scripts/run_opencode_e2e.py --verify-runtime
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Run all six scenarios and retain complete evidence:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
HF_HUB_OFFLINE=1 \
|
|
347
|
+
TRANSFORMERS_OFFLINE=1 \
|
|
348
|
+
HF_DATASETS_OFFLINE=1 \
|
|
349
|
+
PIP_NO_INDEX=1 \
|
|
350
|
+
UV_OFFLINE=1 \
|
|
351
|
+
OPENCODE_DISABLE_AUTOUPDATE=1 \
|
|
352
|
+
python scripts/run_opencode_e2e.py \
|
|
353
|
+
--artifacts artifacts/opencode-ornith-vllm
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
The harness stores server logs, isolated OpenCode stdout/stderr, exported sessions, extracted tool calls, runtime metadata, and per-scenario assertions below the selected artifact directory. It probes `/v1/chat/completions`; a successful `/v1/models` response alone is not accepted as server readiness.
|
|
357
|
+
|
|
358
|
+
For a diagnostic run of one scenario, keep the same release checks and add a selector:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
python scripts/run_opencode_e2e.py \
|
|
362
|
+
--scenario python \
|
|
363
|
+
--artifacts artifacts/opencode-ornith-python
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
The pinned Metal runtime uses `VLLM_METAL_MEMORY_FRACTION=0.90`. Before launch, the harness waits for at least 20.5 GB of available unified memory. The 24,576-token context keeps enough room for OpenCode's system prompt, mapped source evidence, tool-calling rounds, and up to 4,096 output tokens on the 32 GB reference Mac. OpenCode receives this context limit directly from the same runtime lock as vLLM, so client and server limits cannot drift. The harness supplies an explicit session title so OpenCode does not spend the single local inference slot generating one, and it runs the restricted agent deterministically with a bounded iteration count. The server log must show a KV-cache capacity above 24,576 tokens.
|
|
367
|
+
|
|
368
|
+
## Development and Release Gates
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
python -m pytest -q
|
|
372
|
+
ruff check src tests scripts
|
|
373
|
+
mypy src scripts
|
|
374
|
+
python scripts/audit_language_matrix.py
|
|
375
|
+
python -m build
|
|
376
|
+
python -m twine check dist/*
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Release artifacts must be tested from a fresh virtual environment. The wheel must contain the skill/plugin templates and must not contain, depend on, or import `python-delphi-lsp`.
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
Copyright is attributed to `Dark Light`. The package is licensed under MPL-2.0. Ported Delphi frontend files remain under the same license; `python-delphi-lsp` is not installed, imported, bundled as a subpackage, or modified by this project.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
agent_codinglanguage_mapper/__init__.py,sha256=obrrVAfq6Y_Qh5UHZ8YuJWV6BDdiofqAvoVWr5RHru0,448
|
|
2
|
+
agent_codinglanguage_mapper/__main__.py,sha256=D31U8_ux95qF64EQ8ReT25nabzxh7ped5avpobXz_bM,49
|
|
3
|
+
agent_codinglanguage_mapper/_version.py,sha256=uyfGiipFnhOCQlqywx7wsgp6d-SYnqPqsPQd_xePZl8,23
|
|
4
|
+
agent_codinglanguage_mapper/cli.py,sha256=2y-Ued0B57inojA_xOAThaBLJ_q1xTqo8bC2wAOosRE,5422
|
|
5
|
+
agent_codinglanguage_mapper/discovery.py,sha256=rzZ3iSOrFZ3eXXCRFTw0rN2yxoW9Z1SSxIZrhPlf1Y8,50756
|
|
6
|
+
agent_codinglanguage_mapper/indexer.py,sha256=-s_3Yyq_D26ljOcDMAlJ-QyG05w79qfHnYk5spzuL7c,39260
|
|
7
|
+
agent_codinglanguage_mapper/integration.py,sha256=0lFo4oXOtkrOy9iJS1OCvSO0hBhrYDDPUUlJJSzcgrE,6976
|
|
8
|
+
agent_codinglanguage_mapper/lsp_server.py,sha256=LtXalnGP9mmwdCHudCHE80lkR-CO-zcg575dGbX-xgA,15123
|
|
9
|
+
agent_codinglanguage_mapper/lsp_service.py,sha256=M-ACQT45Yii57ObbJAVYttgtS3NhZw35kaIIxREPvC8,19031
|
|
10
|
+
agent_codinglanguage_mapper/mapper.py,sha256=iBhau3-jehmNawoeN-TAaYXMc6_sEovraoSuMSSbINM,25474
|
|
11
|
+
agent_codinglanguage_mapper/models.py,sha256=YCMp0tmOyCqyaAH7XKgUdGAfr9wskdbJYMrtT3Pafd8,2083
|
|
12
|
+
agent_codinglanguage_mapper/protocol.py,sha256=SXAJUzdi2b8L1amQgmT9Fd3SpeZ-Hr-jSvA5uZlCLqs,6421
|
|
13
|
+
agent_codinglanguage_mapper/rendering.py,sha256=rqmlpHp3vxwoh-tES1X7VBy3vr30atGyHQ6egz80rEY,6054
|
|
14
|
+
agent_codinglanguage_mapper/worker.py,sha256=6wcIDJ2SNUASDD5DJngm5SrOurOw52GWj52AhGchC8Q,1140
|
|
15
|
+
agent_codinglanguage_mapper/workspace.py,sha256=5vPgt7h9YQcKgVkVDq7m8dUzGuuN4y5UL9aPhhOLozA,4724
|
|
16
|
+
agent_codinglanguage_mapper/adapters/__init__.py,sha256=qLG_Oz2EtTDGXjkyCFaFJjfifbyeF0HOBDJMQV9HB5U,401
|
|
17
|
+
agent_codinglanguage_mapper/adapters/delphi.py,sha256=9POkTqsS0DqkAQGR1LWZVuS5zQ4dRG4AdQKoWFAjaY0,33645
|
|
18
|
+
agent_codinglanguage_mapper/adapters/tree_sitter.py,sha256=A3l5kVWzyNu362Zft-fqvKOv4EomUs7YlQKDQ2CuN3E,71786
|
|
19
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/__init__.py,sha256=Y7__997bmRQ7BKZoM0qLbp6S6s5W5m8se7ILL7Vw0Js,199
|
|
20
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/comment_builder.py,sha256=XvFW7WTbYy7yGN3MG40gtabTvhGj1ea1MYkySDbVMDA,832
|
|
21
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/consts.py,sha256=qM40iwxXmvEileWHJVLMYlQSkLoLyQaqM-Zscz3VJbs,5958
|
|
22
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/grammar.py,sha256=QWEMSUKr0e1xlJOveZtp7K-MvhlkvDgxkD0iUKIyxKU,18577
|
|
23
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/lark_builder.py,sha256=KTXxr5WrLMvzdb0F71JMzFqExLiEPF1_cwGrZkurgqQ,118651
|
|
24
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/lark_tokens.py,sha256=xT_GES2z6p1IdN3EnoKE9RcUM6suu1RV3J-0FEJN2r8,4176
|
|
25
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/lsp_server.py,sha256=fznUAhcvwTgdiiDrv1nEqW_UsiOJ6Lldd_x-ooGSCLk,65940
|
|
26
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/nodes.py,sha256=LW8KUgNCg9MHK_2NmzrgCMMJNcBwa4C76XwKoS7kt4E,13556
|
|
27
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/parser.py,sha256=-NGETYDomzko-OpQtCLEGUfGpxQKVKeLp23_FY1DL1M,6559
|
|
28
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/preprocessor.py,sha256=7cEfbN7zhWezAiroZ5t-S3H22v4aNTcimQRAigWMvtw,36727
|
|
29
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/project_discovery.py,sha256=ei7Iy19EMpTWjOFpKPc9hDG2oH1MUGxKwCLnibJp_TM,17336
|
|
30
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/project_indexer.py,sha256=R5oD4sYsIi2nAlOCr-Mq3-CFkINeC19t0vtKkciCRmg,11227
|
|
31
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/semantic.py,sha256=FZ9x_QsIS_u-1DlzVlYgRqH9gQZ5ySnDAD-xHwzblU8,9808
|
|
32
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/semantic_builder.py,sha256=ulA7xHJpltjc6cTo5wh9yTcYazuIykM-YLMXD1h-wzk,58680
|
|
33
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/source_reader.py,sha256=HWiw25sLVZ6s1GGpMl17uZ-o68HoxTEd3Z0__m1vsSE,520
|
|
34
|
+
agent_codinglanguage_mapper/adapters/delphi_frontend/workspace.py,sha256=TU__SujItRlW8hB2kuYC7ZJx1WQVOj7frYxvo7rDqeI,2137
|
|
35
|
+
agent_codinglanguage_mapper/templates/opencode/plugins/codebase_map.ts,sha256=RdXvLtL9ieyrhkxv3khbrEjXBeroVyZVSAG4dAzKYk4,6838
|
|
36
|
+
agent_codinglanguage_mapper/templates/skill/SKILL.md,sha256=pyKVldpHWezuiswG5FKI3Rxpmjc9fEYwAHJ3-W4WjTs,3227
|
|
37
|
+
agent_codinglanguage_mapper-1.0.0.dist-info/licenses/LICENSE,sha256=-rPda9qyJvHAhjCx3ZF-Efy07F4eAg4sFvg6ChOGPoU,16726
|
|
38
|
+
agent_codinglanguage_mapper-1.0.0.dist-info/METADATA,sha256=gI1ywuU2NvjKRVJCN7bw8AeAbYuQxRsVQIRP2v8Ias0,19102
|
|
39
|
+
agent_codinglanguage_mapper-1.0.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
40
|
+
agent_codinglanguage_mapper-1.0.0.dist-info/entry_points.txt,sha256=WaoNMRnakxcGmddrFFn74vcpa66YDQHMgDpXF2lbKsI,79
|
|
41
|
+
agent_codinglanguage_mapper-1.0.0.dist-info/top_level.txt,sha256=EYFF-VOTZBm6V7KzaUxkbs0qVTF3w_WT2bsLc6B3Ry0,28
|
|
42
|
+
agent_codinglanguage_mapper-1.0.0.dist-info/RECORD,,
|