agent-codinglanguage-mapper 1.1.0__tar.gz → 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/PKG-INFO +14 -17
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/README.md +13 -16
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/pyproject.toml +1 -1
- agent_codinglanguage_mapper-1.1.1/src/agent_codinglanguage_mapper/_version.py +1 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/cli.py +8 -1
- agent_codinglanguage_mapper-1.1.1/src/agent_codinglanguage_mapper/integration.py +212 -0
- agent_codinglanguage_mapper-1.1.1/src/agent_codinglanguage_mapper/templates/opencode/agents/agent-codinglanguage-mapper.md +16 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/templates/skill/SKILL.md +1 -1
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper.egg-info/PKG-INFO +14 -17
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper.egg-info/SOURCES.txt +1 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_cli.py +3 -1
- agent_codinglanguage_mapper-1.1.1/tests/test_integration_install.py +299 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_opencode_e2e_harness.py +11 -11
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_package_contract.py +2 -2
- agent_codinglanguage_mapper-1.1.0/src/agent_codinglanguage_mapper/_version.py +0 -1
- agent_codinglanguage_mapper-1.1.0/src/agent_codinglanguage_mapper/integration.py +0 -187
- agent_codinglanguage_mapper-1.1.0/tests/test_integration_install.py +0 -196
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/LICENSE +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/setup.cfg +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/__init__.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/__main__.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/__init__.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/__init__.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/comment_builder.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/consts.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/grammar.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/lark_builder.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/lark_tokens.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/lsp_server.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/nodes.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/parser.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/preprocessor.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/project_discovery.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/project_indexer.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/semantic.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/semantic_builder.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/source_reader.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/delphi_frontend/workspace.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/adapters/tree_sitter.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/discovery.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/formatting.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/guidelines.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/indexer.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/lsp_server.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/lsp_service.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/mapper.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/models.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/protocol.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/rendering.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/templates/opencode/plugins/codebase_map.ts +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/worker.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper/workspace.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper.egg-info/dependency_links.txt +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper.egg-info/entry_points.txt +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper.egg-info/requires.txt +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/src/agent_codinglanguage_mapper.egg-info/top_level.txt +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_adapters.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_delphi_adapter.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_discovery.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_ffi.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_guideline_formatting.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_guidelines.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_indexer.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_language_matrix.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_large_files.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_lsp_server.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_lsp_service.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_mapper_details.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_performance_harness.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_protocol.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_rendering.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_worker.py +0 -0
- {agent_codinglanguage_mapper-1.1.0 → agent_codinglanguage_mapper-1.1.1}/tests/test_workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-codinglanguage-mapper
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Agent-focused semantic mapping and language services for mixed-language codebases.
|
|
5
5
|
Author: Dark Light
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -319,40 +319,37 @@ Example generic client configuration:
|
|
|
319
319
|
|
|
320
320
|
## OpenCode Integration
|
|
321
321
|
|
|
322
|
-
Install the
|
|
322
|
+
Install the package-named skill, Markdown agent, and plugin into a repository:
|
|
323
323
|
|
|
324
324
|
```bash
|
|
325
|
-
codinglanguage-mapper opencode install --target .
|
|
325
|
+
codinglanguage-mapper opencode install --target .
|
|
326
326
|
```
|
|
327
327
|
|
|
328
328
|
This creates:
|
|
329
329
|
|
|
330
330
|
```text
|
|
331
|
-
.agents/skills/
|
|
331
|
+
.agents/skills/agent-codinglanguage-mapper/SKILL.md
|
|
332
|
+
.opencode/agents/agent-codinglanguage-mapper.md
|
|
332
333
|
.opencode/plugins/codebase_map.ts
|
|
333
|
-
opencode.json
|
|
334
334
|
```
|
|
335
335
|
|
|
336
|
-
The generated `
|
|
336
|
+
The generated `agent-codinglanguage-mapper` agent denies all tools by default and permits only its package-named skill, `codebase_map`, and `code_guidelines`. 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.
|
|
337
|
+
|
|
338
|
+
The installer never reads or changes `opencode.json`; user configuration remains
|
|
339
|
+
user-owned. The deprecated `--write`, `--write-config`, and `--write-agent`
|
|
340
|
+
compatibility flags are accepted, but the Markdown agent is installed by
|
|
341
|
+
default and those flags do not enable configuration writes.
|
|
337
342
|
|
|
338
343
|
`code_guidelines` exposes four actions: `detect`, `audit`, `patch`, and `apply`. The first three are read-only. `apply` is rejected unless its request contains `write: true`, so the agent must inspect a patch before it can opt into mutation.
|
|
339
344
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
"permission": {
|
|
343
|
-
"*": "deny",
|
|
344
|
-
"skill": "allow",
|
|
345
|
-
"codebase_map": "allow",
|
|
346
|
-
"code_guidelines": "allow"
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
```
|
|
345
|
+
The agent's YAML frontmatter contains its restricted permissions; no matching
|
|
346
|
+
JSON agent block is required.
|
|
350
347
|
|
|
351
348
|
Run it with:
|
|
352
349
|
|
|
353
350
|
```bash
|
|
354
351
|
opencode run \
|
|
355
|
-
--agent
|
|
352
|
+
--agent agent-codinglanguage-mapper \
|
|
356
353
|
"Find PaymentService, inspect its implementation, and cite the callers. Use only the installed skill and codebase_map."
|
|
357
354
|
```
|
|
358
355
|
|
|
@@ -272,40 +272,37 @@ Example generic client configuration:
|
|
|
272
272
|
|
|
273
273
|
## OpenCode Integration
|
|
274
274
|
|
|
275
|
-
Install the
|
|
275
|
+
Install the package-named skill, Markdown agent, and plugin into a repository:
|
|
276
276
|
|
|
277
277
|
```bash
|
|
278
|
-
codinglanguage-mapper opencode install --target .
|
|
278
|
+
codinglanguage-mapper opencode install --target .
|
|
279
279
|
```
|
|
280
280
|
|
|
281
281
|
This creates:
|
|
282
282
|
|
|
283
283
|
```text
|
|
284
|
-
.agents/skills/
|
|
284
|
+
.agents/skills/agent-codinglanguage-mapper/SKILL.md
|
|
285
|
+
.opencode/agents/agent-codinglanguage-mapper.md
|
|
285
286
|
.opencode/plugins/codebase_map.ts
|
|
286
|
-
opencode.json
|
|
287
287
|
```
|
|
288
288
|
|
|
289
|
-
The generated `
|
|
289
|
+
The generated `agent-codinglanguage-mapper` agent denies all tools by default and permits only its package-named skill, `codebase_map`, and `code_guidelines`. 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.
|
|
290
|
+
|
|
291
|
+
The installer never reads or changes `opencode.json`; user configuration remains
|
|
292
|
+
user-owned. The deprecated `--write`, `--write-config`, and `--write-agent`
|
|
293
|
+
compatibility flags are accepted, but the Markdown agent is installed by
|
|
294
|
+
default and those flags do not enable configuration writes.
|
|
290
295
|
|
|
291
296
|
`code_guidelines` exposes four actions: `detect`, `audit`, `patch`, and `apply`. The first three are read-only. `apply` is rejected unless its request contains `write: true`, so the agent must inspect a patch before it can opt into mutation.
|
|
292
297
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
"permission": {
|
|
296
|
-
"*": "deny",
|
|
297
|
-
"skill": "allow",
|
|
298
|
-
"codebase_map": "allow",
|
|
299
|
-
"code_guidelines": "allow"
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
```
|
|
298
|
+
The agent's YAML frontmatter contains its restricted permissions; no matching
|
|
299
|
+
JSON agent block is required.
|
|
303
300
|
|
|
304
301
|
Run it with:
|
|
305
302
|
|
|
306
303
|
```bash
|
|
307
304
|
opencode run \
|
|
308
|
-
--agent
|
|
305
|
+
--agent agent-codinglanguage-mapper \
|
|
309
306
|
"Find PaymentService, inspect its implementation, and cite the callers. Use only the installed skill and codebase_map."
|
|
310
307
|
```
|
|
311
308
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-codinglanguage-mapper"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.1"
|
|
8
8
|
description = "Agent-focused semantic mapping and language services for mixed-language codebases."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10,<3.15"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.1.1"
|
|
@@ -84,7 +84,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
84
84
|
opencode_install.add_argument("--target", type=Path, default=Path("."))
|
|
85
85
|
opencode_install.add_argument("--python", default=sys.executable)
|
|
86
86
|
opencode_install.add_argument("--force", action="store_true")
|
|
87
|
-
opencode_install.add_argument(
|
|
87
|
+
opencode_install.add_argument(
|
|
88
|
+
"--write-agent",
|
|
89
|
+
"--write",
|
|
90
|
+
"--write-config",
|
|
91
|
+
dest="write_config",
|
|
92
|
+
action="store_true",
|
|
93
|
+
help="deprecated compatibility flag; the Markdown agent is always installed",
|
|
94
|
+
)
|
|
88
95
|
return parser
|
|
89
96
|
|
|
90
97
|
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import stat
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from tempfile import NamedTemporaryFile
|
|
9
|
+
from typing import Final
|
|
10
|
+
|
|
11
|
+
PACKAGE_NAME: Final[str] = "agent-codinglanguage-mapper"
|
|
12
|
+
|
|
13
|
+
SKILL_RELATIVE_PATH: Final[Path] = Path(
|
|
14
|
+
f".agents/skills/{PACKAGE_NAME}/SKILL.md"
|
|
15
|
+
)
|
|
16
|
+
LEGACY_SKILL_RELATIVE_PATH: Final[Path] = Path(
|
|
17
|
+
".agents/skills/mixed-codebase-navigator/SKILL.md"
|
|
18
|
+
)
|
|
19
|
+
LEGACY_SKILL_SHA256: Final[str] = (
|
|
20
|
+
"bbacc23782ca97e54b2f65ddea9fb1bf816f23397dc8572e3d1ef7a60e2709d4"
|
|
21
|
+
)
|
|
22
|
+
OPENCODE_PLUGIN_RELATIVE_PATH: Final[Path] = Path(".opencode/plugins/codebase_map.ts")
|
|
23
|
+
OPENCODE_AGENT_RELATIVE_PATH: Final[Path] = Path(
|
|
24
|
+
f".opencode/agents/{PACKAGE_NAME}.md"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
SKILL_TEMPLATE_PATH: Final[Path] = Path("skill", "SKILL.md")
|
|
28
|
+
OPENCODE_PLUGIN_TEMPLATE_PATH: Final[Path] = Path("opencode", "plugins", "codebase_map.ts")
|
|
29
|
+
OPENCODE_AGENT_TEMPLATE_PATH: Final[Path] = Path(
|
|
30
|
+
"opencode", "agents", f"{PACKAGE_NAME}.md"
|
|
31
|
+
)
|
|
32
|
+
_TEMPLATES_ROOT: Final[Path] = Path(__file__).resolve().parent / "templates"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def install_skill(target: Path, force: bool = False) -> Path:
|
|
36
|
+
target_root = _normalize_target(target)
|
|
37
|
+
legacy_path = _preflight_legacy_skill(target_root, force=force)
|
|
38
|
+
skill_path = _safe_join(target_root, SKILL_RELATIVE_PATH)
|
|
39
|
+
_preflight_overwrite((skill_path,), force=force)
|
|
40
|
+
snapshots = _snapshots(skill_path, legacy_path)
|
|
41
|
+
try:
|
|
42
|
+
_write_atomic_text(skill_path, _load_template(SKILL_TEMPLATE_PATH), force=True)
|
|
43
|
+
_remove_legacy_skill(legacy_path)
|
|
44
|
+
except BaseException:
|
|
45
|
+
_restore_snapshots(snapshots)
|
|
46
|
+
raise
|
|
47
|
+
return skill_path
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def install_opencode(
|
|
51
|
+
target: Path,
|
|
52
|
+
python_executable: str | os.PathLike[str],
|
|
53
|
+
force: bool = False,
|
|
54
|
+
write_config: bool = False,
|
|
55
|
+
) -> tuple[Path, Path, Path]:
|
|
56
|
+
target_root = _normalize_target(target)
|
|
57
|
+
legacy_path = _preflight_legacy_skill(target_root, force=force)
|
|
58
|
+
skill_path = _safe_join(target_root, SKILL_RELATIVE_PATH)
|
|
59
|
+
plugin_path = _safe_join(target_root, OPENCODE_PLUGIN_RELATIVE_PATH)
|
|
60
|
+
agent_path = _safe_join(target_root, OPENCODE_AGENT_RELATIVE_PATH)
|
|
61
|
+
_preflight_overwrite((skill_path, plugin_path, agent_path), force=force)
|
|
62
|
+
rendered_skill = _load_template(SKILL_TEMPLATE_PATH)
|
|
63
|
+
rendered_plugin = _render_plugin(str(python_executable))
|
|
64
|
+
rendered_agent = _load_template(OPENCODE_AGENT_TEMPLATE_PATH)
|
|
65
|
+
snapshots = _snapshots(skill_path, plugin_path, agent_path, legacy_path)
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
_write_atomic_text(skill_path, rendered_skill, force=True)
|
|
69
|
+
_write_atomic_text(plugin_path, rendered_plugin, force=True)
|
|
70
|
+
_write_atomic_text(agent_path, rendered_agent, force=True)
|
|
71
|
+
_remove_legacy_skill(legacy_path)
|
|
72
|
+
except BaseException:
|
|
73
|
+
_restore_snapshots(snapshots)
|
|
74
|
+
raise
|
|
75
|
+
_ = write_config # Deprecated compatibility input; user configuration is never touched.
|
|
76
|
+
return skill_path, plugin_path, agent_path
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _preflight_legacy_skill(target: Path, *, force: bool) -> Path | None:
|
|
80
|
+
legacy_path = target / LEGACY_SKILL_RELATIVE_PATH
|
|
81
|
+
legacy_path.relative_to(target)
|
|
82
|
+
_reject_symlink_components(legacy_path, target)
|
|
83
|
+
if not legacy_path.exists():
|
|
84
|
+
return None
|
|
85
|
+
if not legacy_path.is_file():
|
|
86
|
+
raise FileExistsError(f"Legacy skill path is not a file: {legacy_path}")
|
|
87
|
+
digest = hashlib.sha256(legacy_path.read_bytes()).hexdigest()
|
|
88
|
+
if digest != LEGACY_SKILL_SHA256 and not force:
|
|
89
|
+
raise FileExistsError(
|
|
90
|
+
f"Refusing to remove modified legacy skill without force: {legacy_path}"
|
|
91
|
+
)
|
|
92
|
+
return legacy_path
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _remove_legacy_skill(path: Path | None) -> None:
|
|
96
|
+
if path is None:
|
|
97
|
+
return
|
|
98
|
+
path.unlink()
|
|
99
|
+
try:
|
|
100
|
+
path.parent.rmdir()
|
|
101
|
+
except OSError:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _normalize_target(target: Path) -> Path:
|
|
106
|
+
normalized = target.expanduser().resolve()
|
|
107
|
+
if normalized.exists() and not normalized.is_dir():
|
|
108
|
+
raise NotADirectoryError(f"Target must be a directory: {normalized}")
|
|
109
|
+
normalized.mkdir(parents=True, exist_ok=True)
|
|
110
|
+
return normalized
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _safe_join(target: Path, relative: Path) -> Path:
|
|
114
|
+
destination = target / relative
|
|
115
|
+
destination.relative_to(target)
|
|
116
|
+
_reject_symlink_components(destination, target)
|
|
117
|
+
return destination
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _reject_symlink_components(path: Path, target: Path) -> None:
|
|
121
|
+
current = path
|
|
122
|
+
while current != target:
|
|
123
|
+
if current.is_symlink():
|
|
124
|
+
raise FileExistsError(f"Generated path must not contain a symbolic link: {current}")
|
|
125
|
+
current = current.parent
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _preflight_overwrite(paths: tuple[Path, ...], *, force: bool) -> None:
|
|
129
|
+
for path in paths:
|
|
130
|
+
if path.is_symlink():
|
|
131
|
+
raise FileExistsError(f"Refusing to replace symbolic link: {path}")
|
|
132
|
+
if path.exists() and not path.is_file():
|
|
133
|
+
raise FileExistsError(f"Generated destination is not a file: {path}")
|
|
134
|
+
if path.exists() and not force:
|
|
135
|
+
raise FileExistsError(f"Refusing to overwrite existing file: {path}")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
_FileSnapshot = tuple[bytes, int] | None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _snapshots(*paths: Path | None) -> dict[Path, _FileSnapshot]:
|
|
142
|
+
snapshots: dict[Path, _FileSnapshot] = {}
|
|
143
|
+
for path in paths:
|
|
144
|
+
if path is None or path in snapshots:
|
|
145
|
+
continue
|
|
146
|
+
snapshots[path] = (
|
|
147
|
+
(path.read_bytes(), stat.S_IMODE(path.stat().st_mode))
|
|
148
|
+
if path.exists()
|
|
149
|
+
else None
|
|
150
|
+
)
|
|
151
|
+
return snapshots
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _restore_snapshots(snapshots: dict[Path, _FileSnapshot]) -> None:
|
|
155
|
+
for path, snapshot in reversed(tuple(snapshots.items())):
|
|
156
|
+
if snapshot is None:
|
|
157
|
+
if path.is_symlink() or path.is_file():
|
|
158
|
+
path.unlink()
|
|
159
|
+
continue
|
|
160
|
+
content, mode = snapshot
|
|
161
|
+
_write_atomic_bytes(path, content, force=True, mode=mode)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _load_template(relative_path: Path) -> str:
|
|
165
|
+
template = _TEMPLATES_ROOT / relative_path
|
|
166
|
+
return template.read_text(encoding="utf-8")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _render_plugin(python_executable: str) -> str:
|
|
170
|
+
template = _load_template(OPENCODE_PLUGIN_TEMPLATE_PATH)
|
|
171
|
+
escaped_python_executable = json.dumps(str(python_executable))
|
|
172
|
+
return template.replace("{{PYTHON_EXECUTABLE}}", escaped_python_executable)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _write_atomic_text(path: Path, content: str, force: bool = False) -> None:
|
|
176
|
+
_write_atomic_bytes(path, content.encode(), force=force)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _write_atomic_bytes(
|
|
180
|
+
path: Path,
|
|
181
|
+
content: bytes,
|
|
182
|
+
*,
|
|
183
|
+
force: bool = False,
|
|
184
|
+
mode: int | None = None,
|
|
185
|
+
) -> None:
|
|
186
|
+
if path.is_symlink():
|
|
187
|
+
raise FileExistsError(f"Refusing to replace symbolic link: {path}")
|
|
188
|
+
if path.exists() and not force:
|
|
189
|
+
raise FileExistsError(f"Refusing to overwrite existing file: {path}")
|
|
190
|
+
if path.exists() and not path.is_file():
|
|
191
|
+
raise FileExistsError(f"Generated destination is not a file: {path}")
|
|
192
|
+
|
|
193
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
194
|
+
installed_mode = (
|
|
195
|
+
mode
|
|
196
|
+
if mode is not None
|
|
197
|
+
else stat.S_IMODE(path.stat().st_mode)
|
|
198
|
+
if path.exists()
|
|
199
|
+
else 0o644
|
|
200
|
+
)
|
|
201
|
+
tmp_path: Path | None = None
|
|
202
|
+
try:
|
|
203
|
+
with NamedTemporaryFile("wb", dir=path.parent, delete=False) as handle:
|
|
204
|
+
handle.write(content)
|
|
205
|
+
handle.flush()
|
|
206
|
+
os.fsync(handle.fileno())
|
|
207
|
+
tmp_path = Path(handle.name)
|
|
208
|
+
os.chmod(tmp_path, installed_mode)
|
|
209
|
+
os.replace(tmp_path, path)
|
|
210
|
+
finally:
|
|
211
|
+
if tmp_path is not None and tmp_path.exists():
|
|
212
|
+
tmp_path.unlink()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Navigate mixed-language codebases through agent-codinglanguage-mapper.
|
|
3
|
+
mode: subagent
|
|
4
|
+
permission:
|
|
5
|
+
"*": deny
|
|
6
|
+
skill:
|
|
7
|
+
"*": deny
|
|
8
|
+
agent-codinglanguage-mapper: allow
|
|
9
|
+
codebase_map: allow
|
|
10
|
+
code_guidelines: allow
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
Load `agent-codinglanguage-mapper` first. Use only `skill`, `codebase_map`, and
|
|
14
|
+
`code_guidelines` for source inspection and coding-guideline work. Preserve
|
|
15
|
+
returned citations exactly, keep uncertainty explicit, and never replace
|
|
16
|
+
semantic tool evidence with raw filesystem inspection.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: agent-codinglanguage-mapper
|
|
3
3
|
description: Inspect Python, Rust, C#, Delphi, C++, and mixed-language workspaces through a bounded semantic codebase map without raw filesystem search tools.
|
|
4
4
|
license: MPL-2.0
|
|
5
5
|
compatibility: opencode
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-codinglanguage-mapper
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Agent-focused semantic mapping and language services for mixed-language codebases.
|
|
5
5
|
Author: Dark Light
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -319,40 +319,37 @@ Example generic client configuration:
|
|
|
319
319
|
|
|
320
320
|
## OpenCode Integration
|
|
321
321
|
|
|
322
|
-
Install the
|
|
322
|
+
Install the package-named skill, Markdown agent, and plugin into a repository:
|
|
323
323
|
|
|
324
324
|
```bash
|
|
325
|
-
codinglanguage-mapper opencode install --target .
|
|
325
|
+
codinglanguage-mapper opencode install --target .
|
|
326
326
|
```
|
|
327
327
|
|
|
328
328
|
This creates:
|
|
329
329
|
|
|
330
330
|
```text
|
|
331
|
-
.agents/skills/
|
|
331
|
+
.agents/skills/agent-codinglanguage-mapper/SKILL.md
|
|
332
|
+
.opencode/agents/agent-codinglanguage-mapper.md
|
|
332
333
|
.opencode/plugins/codebase_map.ts
|
|
333
|
-
opencode.json
|
|
334
334
|
```
|
|
335
335
|
|
|
336
|
-
The generated `
|
|
336
|
+
The generated `agent-codinglanguage-mapper` agent denies all tools by default and permits only its package-named skill, `codebase_map`, and `code_guidelines`. 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.
|
|
337
|
+
|
|
338
|
+
The installer never reads or changes `opencode.json`; user configuration remains
|
|
339
|
+
user-owned. The deprecated `--write`, `--write-config`, and `--write-agent`
|
|
340
|
+
compatibility flags are accepted, but the Markdown agent is installed by
|
|
341
|
+
default and those flags do not enable configuration writes.
|
|
337
342
|
|
|
338
343
|
`code_guidelines` exposes four actions: `detect`, `audit`, `patch`, and `apply`. The first three are read-only. `apply` is rejected unless its request contains `write: true`, so the agent must inspect a patch before it can opt into mutation.
|
|
339
344
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
"permission": {
|
|
343
|
-
"*": "deny",
|
|
344
|
-
"skill": "allow",
|
|
345
|
-
"codebase_map": "allow",
|
|
346
|
-
"code_guidelines": "allow"
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
```
|
|
345
|
+
The agent's YAML frontmatter contains its restricted permissions; no matching
|
|
346
|
+
JSON agent block is required.
|
|
350
347
|
|
|
351
348
|
Run it with:
|
|
352
349
|
|
|
353
350
|
```bash
|
|
354
351
|
opencode run \
|
|
355
|
-
--agent
|
|
352
|
+
--agent agent-codinglanguage-mapper \
|
|
356
353
|
"Find PaymentService, inspect its implementation, and cite the callers. Use only the installed skill and codebase_map."
|
|
357
354
|
```
|
|
358
355
|
|
|
@@ -43,6 +43,7 @@ src/agent_codinglanguage_mapper/adapters/delphi_frontend/semantic.py
|
|
|
43
43
|
src/agent_codinglanguage_mapper/adapters/delphi_frontend/semantic_builder.py
|
|
44
44
|
src/agent_codinglanguage_mapper/adapters/delphi_frontend/source_reader.py
|
|
45
45
|
src/agent_codinglanguage_mapper/adapters/delphi_frontend/workspace.py
|
|
46
|
+
src/agent_codinglanguage_mapper/templates/opencode/agents/agent-codinglanguage-mapper.md
|
|
46
47
|
src/agent_codinglanguage_mapper/templates/opencode/plugins/codebase_map.ts
|
|
47
48
|
src/agent_codinglanguage_mapper/templates/skill/SKILL.md
|
|
48
49
|
tests/test_adapters.py
|
|
@@ -26,6 +26,7 @@ def test_parser_exposes_mapper_commands_and_layer_choices() -> None:
|
|
|
26
26
|
assert parser.parse_args(["skill", "install"]).skill_command == "install"
|
|
27
27
|
assert parser.parse_args(["opencode", "install", "--write-config"]).write_config is True
|
|
28
28
|
assert parser.parse_args(["opencode", "install", "--write"]).write_config is True
|
|
29
|
+
assert parser.parse_args(["opencode", "install", "--write-agent"]).write_config is True
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
def test_cli_executes_view_index_doctor_and_installers(tmp_path: Path, capsys) -> None:
|
|
@@ -48,7 +49,8 @@ def test_cli_executes_view_index_doctor_and_installers(tmp_path: Path, capsys) -
|
|
|
48
49
|
assert main(["opencode", "install", "--target", str(target), "--write"]) == 0
|
|
49
50
|
installed = json.loads(capsys.readouterr().out)["installed"]
|
|
50
51
|
assert any(path.endswith("codebase_map.ts") for path in installed)
|
|
51
|
-
assert (
|
|
52
|
+
assert any(path.endswith("agents/agent-codinglanguage-mapper.md") for path in installed)
|
|
53
|
+
assert not (target / "opencode.json").exists()
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
def test_cli_guidelines_detect_audit_patch_and_write(tmp_path: Path, capsys) -> None:
|