python-delphi-lsp 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.
- {python_delphi_lsp-1.1.0/python_delphi_lsp.egg-info → python_delphi_lsp-1.1.1}/PKG-INFO +21 -11
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/README.md +20 -10
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/agent_cli.py +15 -1
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/agent_layers.py +167 -1
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/agent_templates.py +28 -29
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/pyproject.toml +1 -1
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1/python_delphi_lsp.egg-info}/PKG-INFO +21 -11
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_codebase_skill_test.py +13 -5
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_agent_codebase.py +84 -5
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_bootstrap_vllm_codebase_skill.py +12 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_package_metadata.py +2 -2
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/LICENSE +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/MANIFEST.in +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/__init__.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/binary.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/comment_builder.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/consts.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/grammar.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/lark_builder.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/lark_tokens.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/lsp_server.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/nodes.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/parser.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/preprocessor.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/project_discovery.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/project_indexer.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/semantic.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/semantic_builder.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/source_reader.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/workspace.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/delphiast/writer.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/opencode.json +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/SOURCES.txt +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/dependency_links.txt +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/entry_points.txt +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/requires.txt +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/top_level.txt +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/audit_delphi_language_features.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_opencode_test.ps1 +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_opencode_test.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_opencode_test.sh +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/check_ornith_cache.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/generate_release_evidence.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/ollama/ornith-lspctx.Modelfile +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/prepare_ornith_cache.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/run_opencode_lsp_probe.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/start_ornith_vllm.sh +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/summarize_opencode_request_payloads.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/setup.cfg +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/DeprecatedOnConst.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/VariantRecordFieldAttributes.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/alignedrecords.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/constset.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/deprecatedtype.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/dottedtypes.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/endtoken.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/experimentals.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/externalfunction.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/finalizationinitializationexports.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/forwardoverloaded.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/forwardwithoutsemicolon.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/genericconstraints.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/genericinterfacemethoddelegation.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/implementsgenerictype.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/include file2.inc +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/includefile.inc +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/includefile.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/managedrecords.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/messagemethod.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/multiline.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/nonalignedrecords.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/numbers.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/pointerchars.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/properties.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/strictvisibility.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/tryexcept.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/umlauts.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/whitespacearoundifdefcondition.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/library_demo.dpr +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/package_demo.dpk +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/program_demo.dpr +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_advanced.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_attributes.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_basic.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_consumer.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_generics.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_inheritance.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_math.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_properties.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_sections.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_statements.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_types.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_unresolved.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_with.pas +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_bootstrap_vllm_opencode.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_diagnostics.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_language_feature_matrix.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_legacy_snippets.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_lsp_features.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_lsp_support.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_opencode_config.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_opencode_probe.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_opencode_request_payloads.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_ornith_cache_prepare.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_parser.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_preprocessor.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_project_discovery.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_project_indexer.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_release_evidence.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_semantic.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_vllm_start_script.py +0 -0
- {python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_workspace.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-delphi-lsp
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Python Delphi/Object Pascal parser, semantic indexer, and language server.
|
|
5
5
|
Author: Dark Light
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -191,13 +191,19 @@ delphi-lsp-agent view --root . --layer overview
|
|
|
191
191
|
delphi-lsp-agent view --root . --layer projects
|
|
192
192
|
delphi-lsp-agent view --root . --layer unit --query Mega100kUnit
|
|
193
193
|
delphi-lsp-agent view --root . --layer symbols --query TWorker --format json
|
|
194
|
+
delphi-lsp-agent view --root . --layer implementation --query TWorker.Run
|
|
195
|
+
delphi-lsp-agent view --root . --layer implementation --query TWorker --format json
|
|
194
196
|
delphi-lsp-agent view --root . --layer problems
|
|
195
197
|
```
|
|
196
198
|
|
|
197
199
|
Available layers are `overview`, `projects`, `units`, `unit`, `symbols`,
|
|
198
|
-
`symbol`, `references`, and `problems`. The output includes
|
|
199
|
-
citations, declarations, ownership, visibility, type information,
|
|
200
|
-
dependency/problem summaries.
|
|
200
|
+
`symbol`, `implementation`, `references`, and `problems`. The output includes
|
|
201
|
+
file and line citations, declarations, ownership, visibility, type information,
|
|
202
|
+
and dependency/problem summaries. Outline layers keep routine bodies out of the
|
|
203
|
+
agent context. Use `implementation` with a concrete class, routine, or member
|
|
204
|
+
query to read only that complete source fragment from the original file. For a
|
|
205
|
+
class query, the layer returns the class declaration plus matching method
|
|
206
|
+
implementations; for a routine query, it returns the full routine body.
|
|
201
207
|
|
|
202
208
|
## opencode Usage
|
|
203
209
|
|
|
@@ -380,13 +386,17 @@ python scripts/bootstrap_vllm_codebase_skill_test.py --start-vllm
|
|
|
380
386
|
```
|
|
381
387
|
|
|
382
388
|
This skill probe defaults the vLLM helper to `MAX_MODEL_LEN=32768`, which was
|
|
383
|
-
the stable local Metal setting for the
|
|
384
|
-
call. Override with `--max-model-len` when your machine
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
the stable local Metal setting for the reduced codebase-agent prompt plus the
|
|
390
|
+
`delphi_codebase` tool call. Override with `--max-model-len` when your machine
|
|
391
|
+
has enough headroom.
|
|
392
|
+
|
|
393
|
+
The probe verifies that the `delphi-codebase-navigator` skill is installed, then
|
|
394
|
+
requires opencode to call `delphi_codebase`, find `MegaProc02500`, and read the
|
|
395
|
+
method body through the `implementation` layer. The vLLM test agent keeps
|
|
396
|
+
opencode's generic `skill` tool disabled so the model context stays focused on
|
|
397
|
+
the Delphi navigation tool. The probe fails if opencode uses `bash`, `read`,
|
|
398
|
+
`glob`, `grep`, `edit`, `write`, `task`, `webfetch`, or `todowrite` before the
|
|
399
|
+
required evidence is complete.
|
|
390
400
|
|
|
391
401
|
## Verification
|
|
392
402
|
|
|
@@ -152,13 +152,19 @@ delphi-lsp-agent view --root . --layer overview
|
|
|
152
152
|
delphi-lsp-agent view --root . --layer projects
|
|
153
153
|
delphi-lsp-agent view --root . --layer unit --query Mega100kUnit
|
|
154
154
|
delphi-lsp-agent view --root . --layer symbols --query TWorker --format json
|
|
155
|
+
delphi-lsp-agent view --root . --layer implementation --query TWorker.Run
|
|
156
|
+
delphi-lsp-agent view --root . --layer implementation --query TWorker --format json
|
|
155
157
|
delphi-lsp-agent view --root . --layer problems
|
|
156
158
|
```
|
|
157
159
|
|
|
158
160
|
Available layers are `overview`, `projects`, `units`, `unit`, `symbols`,
|
|
159
|
-
`symbol`, `references`, and `problems`. The output includes
|
|
160
|
-
citations, declarations, ownership, visibility, type information,
|
|
161
|
-
dependency/problem summaries.
|
|
161
|
+
`symbol`, `implementation`, `references`, and `problems`. The output includes
|
|
162
|
+
file and line citations, declarations, ownership, visibility, type information,
|
|
163
|
+
and dependency/problem summaries. Outline layers keep routine bodies out of the
|
|
164
|
+
agent context. Use `implementation` with a concrete class, routine, or member
|
|
165
|
+
query to read only that complete source fragment from the original file. For a
|
|
166
|
+
class query, the layer returns the class declaration plus matching method
|
|
167
|
+
implementations; for a routine query, it returns the full routine body.
|
|
162
168
|
|
|
163
169
|
## opencode Usage
|
|
164
170
|
|
|
@@ -341,13 +347,17 @@ python scripts/bootstrap_vllm_codebase_skill_test.py --start-vllm
|
|
|
341
347
|
```
|
|
342
348
|
|
|
343
349
|
This skill probe defaults the vLLM helper to `MAX_MODEL_LEN=32768`, which was
|
|
344
|
-
the stable local Metal setting for the
|
|
345
|
-
call. Override with `--max-model-len` when your machine
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
350
|
+
the stable local Metal setting for the reduced codebase-agent prompt plus the
|
|
351
|
+
`delphi_codebase` tool call. Override with `--max-model-len` when your machine
|
|
352
|
+
has enough headroom.
|
|
353
|
+
|
|
354
|
+
The probe verifies that the `delphi-codebase-navigator` skill is installed, then
|
|
355
|
+
requires opencode to call `delphi_codebase`, find `MegaProc02500`, and read the
|
|
356
|
+
method body through the `implementation` layer. The vLLM test agent keeps
|
|
357
|
+
opencode's generic `skill` tool disabled so the model context stays focused on
|
|
358
|
+
the Delphi navigation tool. The probe fails if opencode uses `bash`, `read`,
|
|
359
|
+
`glob`, `grep`, `edit`, `write`, `task`, `webfetch`, or `todowrite` before the
|
|
360
|
+
required evidence is complete.
|
|
351
361
|
|
|
352
362
|
## Verification
|
|
353
363
|
|
|
@@ -19,7 +19,21 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
19
19
|
view = subcommands.add_parser("view", help="Render a layered codebase view.")
|
|
20
20
|
view.add_argument("--root", type=Path, default=Path("."))
|
|
21
21
|
view.add_argument("--project-file", type=Path)
|
|
22
|
-
view.add_argument(
|
|
22
|
+
view.add_argument(
|
|
23
|
+
"--layer",
|
|
24
|
+
required=True,
|
|
25
|
+
choices=[
|
|
26
|
+
"overview",
|
|
27
|
+
"projects",
|
|
28
|
+
"units",
|
|
29
|
+
"unit",
|
|
30
|
+
"symbols",
|
|
31
|
+
"symbol",
|
|
32
|
+
"implementation",
|
|
33
|
+
"references",
|
|
34
|
+
"problems",
|
|
35
|
+
],
|
|
36
|
+
)
|
|
23
37
|
view.add_argument("--query", default="")
|
|
24
38
|
view.add_argument("--format", default="markdown", choices=["markdown", "json"])
|
|
25
39
|
view.add_argument("--deep-projects", action="store_true", help="Deep-parse project dependencies for the projects layer.")
|
|
@@ -8,7 +8,7 @@ import json
|
|
|
8
8
|
from .lsp_server import build_outline_semantic_model, outline_large_source
|
|
9
9
|
from .project_discovery import DelphiProjectDiscovery, discover_delphi_project
|
|
10
10
|
from .project_indexer import ProjectIndexResult, ProjectIndexer
|
|
11
|
-
from .semantic import Scope, SourceRange, Symbol, SymbolIndex
|
|
11
|
+
from .semantic import Scope, SourceRange, Symbol, SymbolIndex, SymbolKind
|
|
12
12
|
from .semantic_builder import SemanticModel
|
|
13
13
|
from .source_reader import read_source_text
|
|
14
14
|
|
|
@@ -92,6 +92,8 @@ def layer_payload(index: CodebaseIndex, layer: str, *, query: str = "") -> dict[
|
|
|
92
92
|
return _symbols_payload(index, query=query)
|
|
93
93
|
if normalized_layer == "symbol":
|
|
94
94
|
return _symbol_payload(index, query=query)
|
|
95
|
+
if normalized_layer == "implementation":
|
|
96
|
+
return _implementation_payload(index, query=query)
|
|
95
97
|
if normalized_layer == "references":
|
|
96
98
|
return _references_payload(index, query=query)
|
|
97
99
|
if normalized_layer == "problems":
|
|
@@ -206,6 +208,142 @@ def _symbol_payload(index: CodebaseIndex, *, query: str) -> dict[str, Any]:
|
|
|
206
208
|
return {"layer": "symbol", "root": index.root, "query": query, "items": matches[:50]}
|
|
207
209
|
|
|
208
210
|
|
|
211
|
+
def _implementation_payload(index: CodebaseIndex, *, query: str) -> dict[str, Any]:
|
|
212
|
+
needle = query.casefold().strip()
|
|
213
|
+
if not needle:
|
|
214
|
+
return {
|
|
215
|
+
"layer": "implementation",
|
|
216
|
+
"root": index.root,
|
|
217
|
+
"query": query,
|
|
218
|
+
"items": [],
|
|
219
|
+
"message": "Pass a class, routine, or member name in query to read focused source.",
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
matches = [symbol for symbol in _all_symbols(index) if symbol.name.casefold() == needle]
|
|
223
|
+
if not matches:
|
|
224
|
+
matches = [
|
|
225
|
+
symbol
|
|
226
|
+
for symbol in _all_symbols(index)
|
|
227
|
+
if symbol.kind.value != "unit" and needle in symbol.name.casefold()
|
|
228
|
+
]
|
|
229
|
+
|
|
230
|
+
items: list[dict[str, Any]] = []
|
|
231
|
+
source_cache: dict[str, list[str]] = {}
|
|
232
|
+
seen_keys: set[tuple[str, str, int, str]] = set()
|
|
233
|
+
for symbol in matches:
|
|
234
|
+
item = _implementation_item(index, symbol, source_cache)
|
|
235
|
+
if item is None:
|
|
236
|
+
continue
|
|
237
|
+
key = (item["name"].casefold(), item["path"], item["line"], item["kind"])
|
|
238
|
+
if key in seen_keys:
|
|
239
|
+
continue
|
|
240
|
+
seen_keys.add(key)
|
|
241
|
+
items.append(item)
|
|
242
|
+
items.sort(key=lambda item: (item["path"].casefold(), item["line"], item["name"].casefold()))
|
|
243
|
+
return {"layer": "implementation", "root": index.root, "query": query, "items": items[:50]}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _implementation_item(
|
|
247
|
+
index: CodebaseIndex,
|
|
248
|
+
symbol: Symbol,
|
|
249
|
+
source_cache: dict[str, list[str]],
|
|
250
|
+
) -> dict[str, Any] | None:
|
|
251
|
+
fragments: list[dict[str, Any]] = []
|
|
252
|
+
if symbol.kind in {SymbolKind.CLASS, SymbolKind.RECORD, SymbolKind.INTERFACE}:
|
|
253
|
+
declaration = _source_fragment(symbol.decl_range, "declaration", source_cache)
|
|
254
|
+
if declaration is not None:
|
|
255
|
+
fragments.append(declaration)
|
|
256
|
+
fragments.extend(_implementation_fragments_for_type(index, symbol, source_cache))
|
|
257
|
+
elif symbol.kind in _ROUTINE_KINDS:
|
|
258
|
+
fragment = _source_fragment(symbol.decl_range, "implementation", source_cache)
|
|
259
|
+
if fragment is not None:
|
|
260
|
+
fragments.append(fragment)
|
|
261
|
+
else:
|
|
262
|
+
fragment = _source_fragment(symbol.decl_range, "declaration", source_cache)
|
|
263
|
+
if fragment is not None:
|
|
264
|
+
fragments.append(fragment)
|
|
265
|
+
fragments.extend(_implementation_fragments_for_member(index, symbol, source_cache))
|
|
266
|
+
if not fragments:
|
|
267
|
+
return None
|
|
268
|
+
item = _symbol_item(symbol)
|
|
269
|
+
item["fragments"] = fragments
|
|
270
|
+
return item
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _implementation_fragments_for_type(
|
|
274
|
+
index: CodebaseIndex,
|
|
275
|
+
symbol: Symbol,
|
|
276
|
+
source_cache: dict[str, list[str]],
|
|
277
|
+
) -> list[dict[str, Any]]:
|
|
278
|
+
prefix = f"{symbol.name.casefold()}."
|
|
279
|
+
fragments: list[dict[str, Any]] = []
|
|
280
|
+
seen_ranges: set[SourceRange] = set()
|
|
281
|
+
for candidate in _all_symbols(index):
|
|
282
|
+
if candidate.kind not in _ROUTINE_KINDS:
|
|
283
|
+
continue
|
|
284
|
+
if candidate.decl_range.file_name != symbol.decl_range.file_name:
|
|
285
|
+
continue
|
|
286
|
+
if not candidate.name.casefold().startswith(prefix):
|
|
287
|
+
continue
|
|
288
|
+
fragment = _source_fragment(candidate.decl_range, "implementation", source_cache)
|
|
289
|
+
if fragment is None or candidate.decl_range in seen_ranges:
|
|
290
|
+
continue
|
|
291
|
+
seen_ranges.add(candidate.decl_range)
|
|
292
|
+
fragment["symbol"] = candidate.name
|
|
293
|
+
fragments.append(fragment)
|
|
294
|
+
fragments.sort(key=lambda item: (item["range"]["start_line"], item["range"]["start_col"]))
|
|
295
|
+
return fragments
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _implementation_fragments_for_member(
|
|
299
|
+
index: CodebaseIndex,
|
|
300
|
+
symbol: Symbol,
|
|
301
|
+
source_cache: dict[str, list[str]],
|
|
302
|
+
) -> list[dict[str, Any]]:
|
|
303
|
+
owner = symbol.scope.owner
|
|
304
|
+
if owner is None or owner.kind not in {SymbolKind.CLASS, SymbolKind.RECORD, SymbolKind.INTERFACE}:
|
|
305
|
+
return []
|
|
306
|
+
qualified_name = f"{owner.name.casefold()}.{symbol.name.casefold()}"
|
|
307
|
+
fragments: list[dict[str, Any]] = []
|
|
308
|
+
for candidate in _all_symbols(index):
|
|
309
|
+
if candidate.kind not in _ROUTINE_KINDS:
|
|
310
|
+
continue
|
|
311
|
+
if candidate.decl_range.file_name != symbol.decl_range.file_name:
|
|
312
|
+
continue
|
|
313
|
+
if candidate.name.casefold() != qualified_name:
|
|
314
|
+
continue
|
|
315
|
+
fragment = _source_fragment(candidate.decl_range, "implementation", source_cache)
|
|
316
|
+
if fragment is not None:
|
|
317
|
+
fragment["symbol"] = candidate.name
|
|
318
|
+
fragments.append(fragment)
|
|
319
|
+
return fragments
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _source_fragment(
|
|
323
|
+
source_range: SourceRange,
|
|
324
|
+
fragment_kind: str,
|
|
325
|
+
source_cache: dict[str, list[str]],
|
|
326
|
+
) -> dict[str, Any] | None:
|
|
327
|
+
lines = source_cache.get(source_range.file_name)
|
|
328
|
+
if lines is None:
|
|
329
|
+
try:
|
|
330
|
+
text = read_source_text(Path(source_range.file_name))
|
|
331
|
+
except (OSError, UnicodeError):
|
|
332
|
+
return None
|
|
333
|
+
lines = text.splitlines(keepends=True)
|
|
334
|
+
source_cache[source_range.file_name] = lines
|
|
335
|
+
if source_range.start_line < 1 or source_range.start_line > len(lines):
|
|
336
|
+
return None
|
|
337
|
+
end_line = min(max(source_range.end_line, source_range.start_line), len(lines))
|
|
338
|
+
snippet = "".join(lines[source_range.start_line - 1 : end_line]).rstrip("\r\n")
|
|
339
|
+
return {
|
|
340
|
+
"fragment_kind": fragment_kind,
|
|
341
|
+
"range": _range_item(source_range),
|
|
342
|
+
"line_count": end_line - source_range.start_line + 1,
|
|
343
|
+
"text": snippet,
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
|
|
209
347
|
def _references_payload(index: CodebaseIndex, *, query: str) -> dict[str, Any]:
|
|
210
348
|
needle = query.casefold().strip()
|
|
211
349
|
items = []
|
|
@@ -265,6 +403,14 @@ def _symbol_item(symbol: Symbol) -> dict[str, Any]:
|
|
|
265
403
|
}
|
|
266
404
|
|
|
267
405
|
|
|
406
|
+
_ROUTINE_KINDS = {
|
|
407
|
+
SymbolKind.PROCEDURE,
|
|
408
|
+
SymbolKind.FUNCTION,
|
|
409
|
+
SymbolKind.CONSTRUCTOR,
|
|
410
|
+
SymbolKind.DESTRUCTOR,
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
|
|
268
414
|
def _range_item(source_range: SourceRange) -> dict[str, int | str]:
|
|
269
415
|
return {
|
|
270
416
|
"path": source_range.file_name,
|
|
@@ -312,6 +458,26 @@ def _render_markdown(payload: dict[str, Any]) -> str:
|
|
|
312
458
|
lines.append(
|
|
313
459
|
f"- `{symbol['name']}` {symbol['kind']} at `{symbol['path']}:{symbol['line']}`"
|
|
314
460
|
)
|
|
461
|
+
elif payload["layer"] == "implementation":
|
|
462
|
+
if payload.get("message"):
|
|
463
|
+
lines.append(payload["message"])
|
|
464
|
+
for item in payload["items"]:
|
|
465
|
+
lines.append(f"## {item['name']}")
|
|
466
|
+
lines.append(f"- Kind: `{item['kind']}`")
|
|
467
|
+
lines.append(f"- Path: `{item['path']}`")
|
|
468
|
+
for fragment in item["fragments"]:
|
|
469
|
+
source_range = fragment["range"]
|
|
470
|
+
lines.append("")
|
|
471
|
+
lines.append(
|
|
472
|
+
f"### {fragment['fragment_kind']} `{source_range['path']}:{source_range['start_line']}`"
|
|
473
|
+
)
|
|
474
|
+
if fragment.get("symbol"):
|
|
475
|
+
lines.append(f"- Symbol: `{fragment['symbol']}`")
|
|
476
|
+
lines.append(f"- Lines: {fragment['line_count']}")
|
|
477
|
+
lines.append("")
|
|
478
|
+
lines.append("```pascal")
|
|
479
|
+
lines.append(fragment["text"])
|
|
480
|
+
lines.append("```")
|
|
315
481
|
elif payload["layer"] == "projects":
|
|
316
482
|
for item in payload["items"]:
|
|
317
483
|
lines.append(f"## `{item['path']}`")
|
|
@@ -58,6 +58,7 @@ Use this skill when a task asks you to understand a Delphi/Object Pascal codebas
|
|
|
58
58
|
- Do not use shell text-search commands for Delphi navigation.
|
|
59
59
|
- Use the `delphi_codebase` opencode tool when it is available.
|
|
60
60
|
- If the tool is not available, run `delphi-lsp-agent view` commands only after the user permits command execution.
|
|
61
|
+
- When a task asks for codebase inspection, call `delphi_codebase` first; do not write "let me inspect" text before the tool call.
|
|
61
62
|
- Cite files and line numbers from the layer output.
|
|
62
63
|
|
|
63
64
|
## Workflow
|
|
@@ -67,14 +68,16 @@ Use this skill when a task asks you to understand a Delphi/Object Pascal codebas
|
|
|
67
68
|
3. Use `units` and `unit` to inspect outlines without routine bodies.
|
|
68
69
|
4. Use `symbols` to find a type, routine, property, field, constant, or variable.
|
|
69
70
|
5. Use `symbol` for a focused symbol card and children.
|
|
70
|
-
6. Use `
|
|
71
|
-
7. Use `
|
|
71
|
+
6. Use `implementation` with a concrete class, routine, or member query when you need the complete source for that one symbol.
|
|
72
|
+
7. Use `references` for definition/reference-oriented evidence.
|
|
73
|
+
8. Use `problems` before concluding that code is missing.
|
|
72
74
|
|
|
73
75
|
## Tool examples
|
|
74
76
|
|
|
75
77
|
- `delphi_codebase({ "layer": "overview" })`
|
|
76
78
|
- `delphi_codebase({ "layer": "unit", "query": "Worker" })`
|
|
77
79
|
- `delphi_codebase({ "layer": "symbols", "query": "TWorker", "format": "json" })`
|
|
80
|
+
- `delphi_codebase({ "layer": "implementation", "query": "TWorker.Run" })`
|
|
78
81
|
|
|
79
82
|
Prefer narrow follow-up queries over broad output.
|
|
80
83
|
"""
|
|
@@ -82,37 +85,26 @@ Prefer narrow follow-up queries over broad output.
|
|
|
82
85
|
|
|
83
86
|
def _opencode_tool(python_executable: str) -> str:
|
|
84
87
|
python_json = json.dumps(python_executable)
|
|
85
|
-
return f"""
|
|
88
|
+
return f"""import {{ tool }} from "@opencode-ai/plugin"
|
|
89
|
+
|
|
90
|
+
const PYTHON = {python_json}
|
|
86
91
|
|
|
87
92
|
async function streamToText(stream: ReadableStream | null): Promise<string> {{
|
|
88
93
|
if (!stream) return ""
|
|
89
94
|
return await new Response(stream).text()
|
|
90
95
|
}}
|
|
91
96
|
|
|
92
|
-
export default {{
|
|
97
|
+
export default tool({{
|
|
93
98
|
description: "Inspect a Delphi/Object Pascal codebase through python-delphi-lsp layered semantic views.",
|
|
94
99
|
args: {{
|
|
95
|
-
layer:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}},
|
|
100
|
-
query: {{
|
|
101
|
-
type: "string",
|
|
102
|
-
description: "Optional unit, file, or symbol filter",
|
|
103
|
-
}},
|
|
104
|
-
root: {{
|
|
105
|
-
type: "string",
|
|
106
|
-
description: "Workspace root; defaults to current worktree or directory",
|
|
107
|
-
}},
|
|
108
|
-
format: {{
|
|
109
|
-
type: "string",
|
|
110
|
-
enum: ["markdown", "json"],
|
|
111
|
-
description: "Output format",
|
|
112
|
-
}},
|
|
100
|
+
layer: tool.schema.enum(["overview", "projects", "units", "unit", "symbols", "symbol", "implementation", "references", "problems"]).describe("Layer to inspect"),
|
|
101
|
+
query: tool.schema.string().optional().describe("Unit, file, or symbol filter"),
|
|
102
|
+
root: tool.schema.string().optional().describe("Workspace root; defaults to current worktree or directory"),
|
|
103
|
+
format: tool.schema.enum(["markdown", "json"]).optional().describe("Output format; defaults to markdown"),
|
|
113
104
|
}},
|
|
114
105
|
async execute(args, context) {{
|
|
115
|
-
const
|
|
106
|
+
const contextDirectory = context.directory && context.directory !== "/" ? context.directory : undefined
|
|
107
|
+
const root = args.root ?? context.worktree ?? contextDirectory ?? process.cwd()
|
|
116
108
|
const command = [
|
|
117
109
|
PYTHON,
|
|
118
110
|
"-m",
|
|
@@ -143,7 +135,7 @@ export default {{
|
|
|
143
135
|
}}
|
|
144
136
|
return stdout.trim()
|
|
145
137
|
}},
|
|
146
|
-
}}
|
|
138
|
+
}})
|
|
147
139
|
"""
|
|
148
140
|
|
|
149
141
|
|
|
@@ -155,10 +147,17 @@ def _merge_opencode_config(config_path: Path) -> None:
|
|
|
155
147
|
agents = config.setdefault("agent", {})
|
|
156
148
|
agents["vllm-delphi-codebase"] = {
|
|
157
149
|
"description": "Use the Delphi codebase navigator skill and tool without direct filesystem source inspection.",
|
|
150
|
+
"temperature": 0,
|
|
151
|
+
"prompt": (
|
|
152
|
+
"You are a tool-calling assistant. When the user asks for a tool call, call the matching tool. "
|
|
153
|
+
"The matching tool for Delphi/Object Pascal codebase inspection is delphi_codebase. "
|
|
154
|
+
"Use delphi_codebase before answering Delphi codebase questions. "
|
|
155
|
+
"Use implementation for complete source of one concrete class, routine, or member."
|
|
156
|
+
),
|
|
158
157
|
"tools": {
|
|
159
158
|
"delphi_codebase": True,
|
|
160
|
-
"skill":
|
|
161
|
-
"lsp":
|
|
159
|
+
"skill": False,
|
|
160
|
+
"lsp": False,
|
|
162
161
|
"bash": False,
|
|
163
162
|
"read": False,
|
|
164
163
|
"glob": False,
|
|
@@ -170,9 +169,9 @@ def _merge_opencode_config(config_path: Path) -> None:
|
|
|
170
169
|
"todowrite": False,
|
|
171
170
|
},
|
|
172
171
|
"permission": {
|
|
173
|
-
"
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
"delphi_codebase": "allow",
|
|
173
|
+
"lsp": "deny",
|
|
174
|
+
"skill": "deny",
|
|
176
175
|
},
|
|
177
176
|
}
|
|
178
177
|
config_path.write_text(json.dumps(config, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-delphi-lsp"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.1"
|
|
8
8
|
description = "Python Delphi/Object Pascal parser, semantic indexer, and language server."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-delphi-lsp
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Python Delphi/Object Pascal parser, semantic indexer, and language server.
|
|
5
5
|
Author: Dark Light
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -191,13 +191,19 @@ delphi-lsp-agent view --root . --layer overview
|
|
|
191
191
|
delphi-lsp-agent view --root . --layer projects
|
|
192
192
|
delphi-lsp-agent view --root . --layer unit --query Mega100kUnit
|
|
193
193
|
delphi-lsp-agent view --root . --layer symbols --query TWorker --format json
|
|
194
|
+
delphi-lsp-agent view --root . --layer implementation --query TWorker.Run
|
|
195
|
+
delphi-lsp-agent view --root . --layer implementation --query TWorker --format json
|
|
194
196
|
delphi-lsp-agent view --root . --layer problems
|
|
195
197
|
```
|
|
196
198
|
|
|
197
199
|
Available layers are `overview`, `projects`, `units`, `unit`, `symbols`,
|
|
198
|
-
`symbol`, `references`, and `problems`. The output includes
|
|
199
|
-
citations, declarations, ownership, visibility, type information,
|
|
200
|
-
dependency/problem summaries.
|
|
200
|
+
`symbol`, `implementation`, `references`, and `problems`. The output includes
|
|
201
|
+
file and line citations, declarations, ownership, visibility, type information,
|
|
202
|
+
and dependency/problem summaries. Outline layers keep routine bodies out of the
|
|
203
|
+
agent context. Use `implementation` with a concrete class, routine, or member
|
|
204
|
+
query to read only that complete source fragment from the original file. For a
|
|
205
|
+
class query, the layer returns the class declaration plus matching method
|
|
206
|
+
implementations; for a routine query, it returns the full routine body.
|
|
201
207
|
|
|
202
208
|
## opencode Usage
|
|
203
209
|
|
|
@@ -380,13 +386,17 @@ python scripts/bootstrap_vllm_codebase_skill_test.py --start-vllm
|
|
|
380
386
|
```
|
|
381
387
|
|
|
382
388
|
This skill probe defaults the vLLM helper to `MAX_MODEL_LEN=32768`, which was
|
|
383
|
-
the stable local Metal setting for the
|
|
384
|
-
call. Override with `--max-model-len` when your machine
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
389
|
+
the stable local Metal setting for the reduced codebase-agent prompt plus the
|
|
390
|
+
`delphi_codebase` tool call. Override with `--max-model-len` when your machine
|
|
391
|
+
has enough headroom.
|
|
392
|
+
|
|
393
|
+
The probe verifies that the `delphi-codebase-navigator` skill is installed, then
|
|
394
|
+
requires opencode to call `delphi_codebase`, find `MegaProc02500`, and read the
|
|
395
|
+
method body through the `implementation` layer. The vLLM test agent keeps
|
|
396
|
+
opencode's generic `skill` tool disabled so the model context stays focused on
|
|
397
|
+
the Delphi navigation tool. The probe fails if opencode uses `bash`, `read`,
|
|
398
|
+
`glob`, `grep`, `edit`, `write`, `task`, `webfetch`, or `todowrite` before the
|
|
399
|
+
required evidence is complete.
|
|
390
400
|
|
|
391
401
|
## Verification
|
|
392
402
|
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_codebase_skill_test.py
RENAMED
|
@@ -101,9 +101,11 @@ def build_probe_command(
|
|
|
101
101
|
) -> list[str]:
|
|
102
102
|
output_path = output or sandbox / "bootstrap_vllm_codebase_skill_probe.jsonl"
|
|
103
103
|
prompt = (
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
104
|
+
"Use only the delphi_codebase tool to inspect the Delphi project. Do not write "
|
|
105
|
+
"any explanatory text before calling that tool. First call delphi_codebase with layer symbols, "
|
|
106
|
+
'query "MegaProc02500", and format json. Then call delphi_codebase with layer implementation, '
|
|
107
|
+
'query "MegaProc02500", and format markdown. After both tool calls, explain which project and '
|
|
108
|
+
"unit contain it with file and line evidence, and cite one statement from the method body."
|
|
107
109
|
)
|
|
108
110
|
return [
|
|
109
111
|
str(python_executable),
|
|
@@ -115,9 +117,9 @@ def build_probe_command(
|
|
|
115
117
|
"--agent",
|
|
116
118
|
DEFAULT_AGENT,
|
|
117
119
|
"--require-tool",
|
|
118
|
-
"skill:delphi-codebase-navigator",
|
|
119
|
-
"--require-tool",
|
|
120
120
|
f"delphi_codebase:{DEFAULT_SYMBOL}",
|
|
121
|
+
"--require-tool",
|
|
122
|
+
"delphi_codebase:Value := Value + 40",
|
|
121
123
|
"--forbid-tool",
|
|
122
124
|
"bash",
|
|
123
125
|
"--forbid-tool",
|
|
@@ -202,6 +204,12 @@ def main() -> int:
|
|
|
202
204
|
wait_for_endpoint(args.base_url, args.ready_timeout, api_key=args.api_key)
|
|
203
205
|
env = os.environ.copy()
|
|
204
206
|
env["OPENCODE_EXPERIMENTAL_LSP_TOOL"] = "true"
|
|
207
|
+
npm_cache = sandbox / ".opencode" / ".npm-cache"
|
|
208
|
+
bun_cache = sandbox / ".opencode" / ".bun-cache"
|
|
209
|
+
npm_cache.mkdir(parents=True, exist_ok=True)
|
|
210
|
+
bun_cache.mkdir(parents=True, exist_ok=True)
|
|
211
|
+
env.setdefault("NPM_CONFIG_CACHE", str(npm_cache))
|
|
212
|
+
env.setdefault("BUN_INSTALL_CACHE_DIR", str(bun_cache))
|
|
205
213
|
run(
|
|
206
214
|
build_probe_command(
|
|
207
215
|
root=ROOT,
|
|
@@ -51,6 +51,32 @@ def make_project(root: Path) -> None:
|
|
|
51
51
|
)
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def make_mega_project(root: Path, *, proc_count: int = 2500, statements_per_proc: int = 40) -> None:
|
|
55
|
+
lines = [
|
|
56
|
+
"unit Mega100kUnit;",
|
|
57
|
+
"",
|
|
58
|
+
"interface",
|
|
59
|
+
"",
|
|
60
|
+
"type",
|
|
61
|
+
" TMegaValue = Integer;",
|
|
62
|
+
"",
|
|
63
|
+
"implementation",
|
|
64
|
+
"",
|
|
65
|
+
]
|
|
66
|
+
for index in range(1, proc_count + 1):
|
|
67
|
+
lines.append(f"procedure MegaProc{index:05d};")
|
|
68
|
+
lines.append("var")
|
|
69
|
+
lines.append(" Value: Integer;")
|
|
70
|
+
lines.append("begin")
|
|
71
|
+
lines.append(" Value := 0;")
|
|
72
|
+
for statement in range(1, statements_per_proc + 1):
|
|
73
|
+
lines.append(f" Value := Value + {statement};")
|
|
74
|
+
lines.append("end;")
|
|
75
|
+
lines.append("")
|
|
76
|
+
lines.append("end.")
|
|
77
|
+
write_text(root / "Mega100kUnit.pas", "\n".join(lines) + "\n")
|
|
78
|
+
|
|
79
|
+
|
|
54
80
|
def test_builds_layered_markdown_without_exposing_routine_bodies(tmp_path: Path) -> None:
|
|
55
81
|
make_project(tmp_path)
|
|
56
82
|
|
|
@@ -63,6 +89,52 @@ def test_builds_layered_markdown_without_exposing_routine_bodies(tmp_path: Path)
|
|
|
63
89
|
assert "body must not be exposed" not in markdown
|
|
64
90
|
|
|
65
91
|
|
|
92
|
+
def test_implementation_layer_exposes_only_queried_method_body(tmp_path: Path) -> None:
|
|
93
|
+
make_project(tmp_path)
|
|
94
|
+
|
|
95
|
+
index = build_codebase_index(tmp_path)
|
|
96
|
+
markdown = render_layer(index, "implementation", query="TWorker.Run", output_format="markdown")
|
|
97
|
+
|
|
98
|
+
assert "TWorker.Run" in markdown
|
|
99
|
+
assert "procedure TWorker.Run;" in markdown
|
|
100
|
+
assert "body must not be exposed in the layer output" in markdown
|
|
101
|
+
assert "unit Worker;" not in markdown
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_implementation_layer_exposes_class_declaration_and_method_bodies(tmp_path: Path) -> None:
|
|
105
|
+
make_project(tmp_path)
|
|
106
|
+
|
|
107
|
+
index = build_codebase_index(tmp_path)
|
|
108
|
+
payload = json.loads(render_layer(index, "implementation", query="TWorker", output_format="json"))
|
|
109
|
+
|
|
110
|
+
assert payload["layer"] == "implementation"
|
|
111
|
+
[item] = payload["items"]
|
|
112
|
+
fragment_kinds = {fragment["fragment_kind"] for fragment in item["fragments"]}
|
|
113
|
+
fragment_text = "\n".join(fragment["text"] for fragment in item["fragments"])
|
|
114
|
+
assert fragment_kinds == {"declaration", "implementation"}
|
|
115
|
+
assert "TWorker = class" in fragment_text
|
|
116
|
+
assert "procedure TWorker.Run;" in fragment_text
|
|
117
|
+
assert "body must not be exposed in the layer output" in fragment_text
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_implementation_layer_slices_single_method_from_100k_line_file(tmp_path: Path) -> None:
|
|
121
|
+
make_mega_project(tmp_path)
|
|
122
|
+
source = (tmp_path / "Mega100kUnit.pas").read_text(encoding="utf-8")
|
|
123
|
+
assert source.count("\n") > 100_000
|
|
124
|
+
|
|
125
|
+
index = build_codebase_index(tmp_path)
|
|
126
|
+
payload = json.loads(render_layer(index, "implementation", query="MegaProc02500", output_format="json"))
|
|
127
|
+
|
|
128
|
+
[item] = payload["items"]
|
|
129
|
+
text = item["fragments"][0]["text"]
|
|
130
|
+
assert item["name"] == "MegaProc02500"
|
|
131
|
+
assert "procedure MegaProc02500;" in text
|
|
132
|
+
assert " Value := Value + 40;" in text
|
|
133
|
+
assert "procedure MegaProc02499;" not in text
|
|
134
|
+
assert "procedure MegaProc02498;" not in text
|
|
135
|
+
assert item["fragments"][0]["range"]["start_line"] > 100_000
|
|
136
|
+
|
|
137
|
+
|
|
66
138
|
def test_default_layer_index_does_not_deep_parse_project_dependencies(tmp_path: Path, monkeypatch) -> None:
|
|
67
139
|
make_project(tmp_path)
|
|
68
140
|
|
|
@@ -91,9 +163,9 @@ def test_agent_cli_outputs_symbol_layer_as_json(tmp_path: Path) -> None:
|
|
|
91
163
|
"--root",
|
|
92
164
|
str(tmp_path),
|
|
93
165
|
"--layer",
|
|
94
|
-
"
|
|
166
|
+
"implementation",
|
|
95
167
|
"--query",
|
|
96
|
-
"TWorker",
|
|
168
|
+
"TWorker.Run",
|
|
97
169
|
"--format",
|
|
98
170
|
"json",
|
|
99
171
|
],
|
|
@@ -103,8 +175,9 @@ def test_agent_cli_outputs_symbol_layer_as_json(tmp_path: Path) -> None:
|
|
|
103
175
|
)
|
|
104
176
|
|
|
105
177
|
payload = json.loads(completed.stdout)
|
|
106
|
-
assert payload["layer"] == "
|
|
107
|
-
assert any(item["name"] == "TWorker" for item in payload["items"])
|
|
178
|
+
assert payload["layer"] == "implementation"
|
|
179
|
+
assert any(item["name"] == "TWorker.Run" for item in payload["items"])
|
|
180
|
+
assert "procedure TWorker.Run;" in completed.stdout
|
|
108
181
|
|
|
109
182
|
|
|
110
183
|
def test_opencode_install_writes_skill_and_custom_tool(tmp_path: Path) -> None:
|
|
@@ -133,6 +206,12 @@ def test_opencode_install_writes_skill_and_custom_tool(tmp_path: Path) -> None:
|
|
|
133
206
|
tool_text = tool.read_text(encoding="utf-8")
|
|
134
207
|
assert sys.executable in tool_text
|
|
135
208
|
assert "delphiast.agent_cli" in tool_text
|
|
136
|
-
assert
|
|
209
|
+
assert '@opencode-ai/plugin' in tool_text
|
|
210
|
+
assert "tool.schema.enum" in tool_text
|
|
211
|
+
assert "tool.schema.string().optional()" in tool_text
|
|
212
|
+
assert 'args.format ?? "markdown"' in tool_text
|
|
213
|
+
assert '"implementation"' in tool_text
|
|
214
|
+
assert 'context.directory !== "/"' in tool_text
|
|
215
|
+
assert "process.cwd()" in tool_text
|
|
137
216
|
assert "grep" not in tool_text.casefold()
|
|
138
217
|
assert str(skill) in completed.stdout
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/test_bootstrap_vllm_codebase_skill.py
RENAMED
|
@@ -31,6 +31,12 @@ def test_writes_codebase_skill_sandbox_with_project_paths(tmp_path: Path) -> Non
|
|
|
31
31
|
config = (tmp_path / "opencode.json").read_text(encoding="utf-8")
|
|
32
32
|
assert "vllm-delphi-codebase" in config
|
|
33
33
|
assert '"delphi_codebase": true' in config
|
|
34
|
+
assert '"skill": false' in config
|
|
35
|
+
assert '"lsp": false' in config
|
|
36
|
+
assert '"delphi_codebase": "allow"' in config
|
|
37
|
+
assert '"skill": "deny"' in config
|
|
38
|
+
assert '"temperature": 0' in config
|
|
39
|
+
assert "When the user asks for a tool call" in config
|
|
34
40
|
assert '"read": false' in config
|
|
35
41
|
|
|
36
42
|
|
|
@@ -42,7 +48,11 @@ def test_probe_command_requires_delphi_codebase_tool_and_forbids_raw_tools(tmp_p
|
|
|
42
48
|
assert command[:2] == [str(python_exe), str(ROOT / "scripts" / "run_opencode_lsp_probe.py")]
|
|
43
49
|
assert "vllm/ornith-lspctx" in command
|
|
44
50
|
assert "vllm-delphi-codebase" in command
|
|
51
|
+
assert "skill:delphi-codebase-navigator" not in command
|
|
45
52
|
assert "delphi_codebase:MegaProc02500" in command
|
|
53
|
+
assert "delphi_codebase:Value := Value + 40" in command
|
|
54
|
+
assert "Do not write any explanatory text before calling that tool" in command[-1]
|
|
55
|
+
assert "layer implementation" in command[-1]
|
|
46
56
|
forbidden = [command[index + 1] for index, item in enumerate(command) if item == "--forbid-tool"]
|
|
47
57
|
assert {"bash", "read", "glob", "grep", "edit", "write", "task", "webfetch", "todowrite"}.issubset(
|
|
48
58
|
set(forbidden)
|
|
@@ -54,3 +64,5 @@ def test_skill_bootstrap_defaults_to_smaller_vllm_context_for_metal_stability()
|
|
|
54
64
|
|
|
55
65
|
assert 'DEFAULT_MAX_MODEL_LEN = "32768"' in script
|
|
56
66
|
assert 'os.environ["MAX_MODEL_LEN"] = args.max_model_len' in script
|
|
67
|
+
assert 'env.setdefault("NPM_CONFIG_CACHE"' in script
|
|
68
|
+
assert 'env.setdefault("BUN_INSTALL_CACHE_DIR"' in script
|
|
@@ -24,11 +24,11 @@ def test_lsp_console_script_dependencies_are_installed_by_default() -> None:
|
|
|
24
24
|
assert '"lsprotocol>=2023.0.1"' in project
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def
|
|
27
|
+
def test_release_metadata_declares_1_1_1_and_windows_support() -> None:
|
|
28
28
|
pyproject = (ROOT / 'pyproject.toml').read_text(encoding='utf-8')
|
|
29
29
|
project = _section('project', pyproject)
|
|
30
30
|
|
|
31
|
-
assert 'version = "1.1.
|
|
31
|
+
assert 'version = "1.1.1"' in project
|
|
32
32
|
assert '"Operating System :: OS Independent"' in project
|
|
33
33
|
assert '"Operating System :: Microsoft :: Windows"' in project
|
|
34
34
|
assert '"Operating System :: MacOS"' in project
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/top_level.txt
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/audit_delphi_language_features.py
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_opencode_test.ps1
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/scripts/summarize_opencode_request_payloads.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/constset.pas
RENAMED
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/dottedtypes.pas
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/endtoken.pas
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/experimentals.pas
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/include file2.inc
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/includefile.inc
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/includefile.pas
RENAMED
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/messagemethod.pas
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/multiline.pas
RENAMED
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/numbers.pas
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/pointerchars.pas
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/properties.pas
RENAMED
|
File without changes
|
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/tryexcept.pas
RENAMED
|
File without changes
|
{python_delphi_lsp-1.1.0 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/umlauts.pas
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|