python-delphi-lsp 1.0.0__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/MANIFEST.in +1 -0
  2. {python_delphi_lsp-1.0.0/python_delphi_lsp.egg-info → python_delphi_lsp-1.1.0}/PKG-INFO +153 -9
  3. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/README.md +152 -8
  4. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/__init__.py +4 -0
  5. python_delphi_lsp-1.1.0/delphiast/agent_cli.py +99 -0
  6. python_delphi_lsp-1.1.0/delphiast/agent_layers.py +331 -0
  7. python_delphi_lsp-1.1.0/delphiast/agent_templates.py +181 -0
  8. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/lsp_server.py +62 -3
  9. python_delphi_lsp-1.1.0/delphiast/project_discovery.py +375 -0
  10. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/opencode.json +1 -5
  11. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/pyproject.toml +2 -1
  12. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0/python_delphi_lsp.egg-info}/PKG-INFO +153 -9
  13. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/python_delphi_lsp.egg-info/SOURCES.txt +8 -0
  14. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/python_delphi_lsp.egg-info/entry_points.txt +1 -0
  15. python_delphi_lsp-1.1.0/scripts/bootstrap_vllm_codebase_skill_test.py +227 -0
  16. python_delphi_lsp-1.1.0/tests/test_agent_codebase.py +138 -0
  17. python_delphi_lsp-1.1.0/tests/test_bootstrap_vllm_codebase_skill.py +56 -0
  18. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_opencode_config.py +9 -0
  19. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_package_metadata.py +6 -2
  20. python_delphi_lsp-1.1.0/tests/test_project_discovery.py +174 -0
  21. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/LICENSE +0 -0
  22. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/binary.py +0 -0
  23. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/comment_builder.py +0 -0
  24. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/consts.py +0 -0
  25. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/grammar.py +0 -0
  26. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/lark_builder.py +0 -0
  27. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/lark_tokens.py +0 -0
  28. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/nodes.py +0 -0
  29. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/parser.py +0 -0
  30. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/preprocessor.py +0 -0
  31. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/project_indexer.py +0 -0
  32. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/semantic.py +0 -0
  33. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/semantic_builder.py +0 -0
  34. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/source_reader.py +0 -0
  35. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/workspace.py +0 -0
  36. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/delphiast/writer.py +0 -0
  37. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/python_delphi_lsp.egg-info/dependency_links.txt +0 -0
  38. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/python_delphi_lsp.egg-info/requires.txt +0 -0
  39. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/python_delphi_lsp.egg-info/top_level.txt +0 -0
  40. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/audit_delphi_language_features.py +0 -0
  41. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/bootstrap_vllm_opencode_test.ps1 +0 -0
  42. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/bootstrap_vllm_opencode_test.py +0 -0
  43. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/bootstrap_vllm_opencode_test.sh +0 -0
  44. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/check_ornith_cache.py +0 -0
  45. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/generate_release_evidence.py +0 -0
  46. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/ollama/ornith-lspctx.Modelfile +0 -0
  47. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/prepare_ornith_cache.py +0 -0
  48. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/run_opencode_lsp_probe.py +0 -0
  49. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/start_ornith_vllm.sh +0 -0
  50. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/scripts/summarize_opencode_request_payloads.py +0 -0
  51. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/setup.cfg +0 -0
  52. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/DeprecatedOnConst.pas +0 -0
  53. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/VariantRecordFieldAttributes.pas +0 -0
  54. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/alignedrecords.pas +0 -0
  55. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/constset.pas +0 -0
  56. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/deprecatedtype.pas +0 -0
  57. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/dottedtypes.pas +0 -0
  58. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/endtoken.pas +0 -0
  59. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/experimentals.pas +0 -0
  60. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/externalfunction.pas +0 -0
  61. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/finalizationinitializationexports.pas +0 -0
  62. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/forwardoverloaded.pas +0 -0
  63. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/forwardwithoutsemicolon.pas +0 -0
  64. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/genericconstraints.pas +0 -0
  65. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/genericinterfacemethoddelegation.pas +0 -0
  66. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/implementsgenerictype.pas +0 -0
  67. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/include file2.inc +0 -0
  68. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/includefile.inc +0 -0
  69. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/includefile.pas +0 -0
  70. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/managedrecords.pas +0 -0
  71. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/messagemethod.pas +0 -0
  72. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/multiline.pas +0 -0
  73. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/nonalignedrecords.pas +0 -0
  74. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/numbers.pas +0 -0
  75. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/pointerchars.pas +0 -0
  76. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/properties.pas +0 -0
  77. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/strictvisibility.pas +0 -0
  78. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/tryexcept.pas +0 -0
  79. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/umlauts.pas +0 -0
  80. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/legacy_snippets/whitespacearoundifdefcondition.pas +0 -0
  81. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/library_demo.dpr +0 -0
  82. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/package_demo.dpk +0 -0
  83. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/program_demo.dpr +0 -0
  84. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_advanced.pas +0 -0
  85. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_attributes.pas +0 -0
  86. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_basic.pas +0 -0
  87. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_consumer.pas +0 -0
  88. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_generics.pas +0 -0
  89. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_inheritance.pas +0 -0
  90. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_math.pas +0 -0
  91. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_properties.pas +0 -0
  92. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_sections.pas +0 -0
  93. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_statements.pas +0 -0
  94. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_types.pas +0 -0
  95. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_unresolved.pas +0 -0
  96. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/fixtures/unit_with.pas +0 -0
  97. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_bootstrap_vllm_opencode.py +0 -0
  98. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_diagnostics.py +0 -0
  99. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_language_feature_matrix.py +0 -0
  100. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_legacy_snippets.py +0 -0
  101. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_lsp_features.py +0 -0
  102. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_lsp_support.py +0 -0
  103. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_opencode_probe.py +0 -0
  104. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_opencode_request_payloads.py +0 -0
  105. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_ornith_cache_prepare.py +0 -0
  106. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_parser.py +0 -0
  107. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_preprocessor.py +0 -0
  108. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_project_indexer.py +0 -0
  109. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_release_evidence.py +0 -0
  110. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_semantic.py +0 -0
  111. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_vllm_start_script.py +0 -0
  112. {python_delphi_lsp-1.0.0 → python_delphi_lsp-1.1.0}/tests/test_workspace.py +0 -0
@@ -4,6 +4,7 @@ include opencode.json
4
4
  include scripts/check_ornith_cache.py
5
5
  include scripts/audit_delphi_language_features.py
6
6
  include scripts/bootstrap_vllm_opencode_test.py
7
+ include scripts/bootstrap_vllm_codebase_skill_test.py
7
8
  include scripts/bootstrap_vllm_opencode_test.ps1
8
9
  include scripts/bootstrap_vllm_opencode_test.sh
9
10
  include scripts/generate_release_evidence.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-delphi-lsp
3
- Version: 1.0.0
3
+ Version: 1.1.0
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
@@ -43,8 +43,9 @@ Python Delphi LSP is a standalone Python package for Delphi/Object Pascal
43
43
  parsing, semantic indexing, diagnostics, and Language Server Protocol support.
44
44
 
45
45
  The distributable package is named `python-delphi-lsp`. The import package keeps
46
- the established `delphiast` name, and the language-server executable is
47
- `delphi-lsp`.
46
+ the established `delphiast` name. The language-server executable is
47
+ `delphi-lsp`, and the agent-facing codebase navigator executable is
48
+ `delphi-lsp-agent`.
48
49
 
49
50
  ## What It Provides
50
51
 
@@ -56,7 +57,10 @@ the established `delphiast` name, and the language-server executable is
56
57
  - Workspace indexing across Delphi projects
57
58
  - LSP support for document symbols, workspace symbols, hover, definition,
58
59
  references, rename, completion, and diagnostics
59
- - opencode integration through the experimental LSP tool
60
+ - Automatic project path discovery from `.dpr`, `.dpk`, `.dproj`, `.cfg`, and
61
+ `.dof` files
62
+ - opencode integration through the experimental LSP tool and an installable
63
+ `.agents` skill with a dedicated codebase-inspection tool
60
64
 
61
65
  ## Installation
62
66
 
@@ -138,17 +142,124 @@ python -m delphiast.lsp_server
138
142
  ```
139
143
 
140
144
  The server expects normal LSP JSON-RPC over stdio. Editors and tools should set
141
- the workspace root to the Delphi project directory and pass any include paths or
142
- compiler defines through LSP initialization options when needed.
145
+ the workspace root to the Delphi project directory.
146
+
147
+ Auto-discovery reads `.dpr`, `.dpk`, `.dproj`, `.cfg`, and `.dof` files to
148
+ collect unit search paths, include paths, defines, direct `Unit in 'path.pas'`
149
+ references, and workspace source directories. Manual initialization options are
150
+ still accepted as overrides, but a normal project should not require users to
151
+ set include or source paths by hand.
152
+
153
+ ```json
154
+ {
155
+ "$schema": "https://opencode.ai/config.json",
156
+ "lsp": {
157
+ "delphi": {
158
+ "command": ["python", "-m", "delphiast.lsp_server"],
159
+ "extensions": [".pas", ".dpr", ".dpk", ".inc"],
160
+ "initialization": {
161
+ "autoDiscoverPaths": true
162
+ }
163
+ }
164
+ }
165
+ }
166
+ ```
167
+
168
+ ## Agent Codebase Navigator
169
+
170
+ Install the opencode skill and custom tool in a Delphi checkout:
171
+
172
+ ```bash
173
+ delphi-lsp-agent opencode install --target . --write-config
174
+ ```
175
+
176
+ This writes:
177
+
178
+ - `.agents/skills/delphi-codebase-navigator/SKILL.md`
179
+ - `.opencode/tools/delphi_codebase.ts`
180
+ - an optional `vllm-delphi-codebase` agent entry in `opencode.json`
181
+
182
+ The skill tells agents to inspect Delphi code through layered semantic views
183
+ instead of loading full source files. The custom opencode tool calls
184
+ `python -m delphiast.agent_cli` directly, so the agent does not need shell text
185
+ search or raw file-reading tools to understand the codebase.
186
+
187
+ Useful direct commands:
188
+
189
+ ```bash
190
+ delphi-lsp-agent view --root . --layer overview
191
+ delphi-lsp-agent view --root . --layer projects
192
+ delphi-lsp-agent view --root . --layer unit --query Mega100kUnit
193
+ delphi-lsp-agent view --root . --layer symbols --query TWorker --format json
194
+ delphi-lsp-agent view --root . --layer problems
195
+ ```
196
+
197
+ Available layers are `overview`, `projects`, `units`, `unit`, `symbols`,
198
+ `symbol`, `references`, and `problems`. The output includes file and line
199
+ citations, declarations, ownership, visibility, type information, and
200
+ dependency/problem summaries. Routine bodies are not emitted.
143
201
 
144
202
  ## opencode Usage
145
203
 
146
204
  This repository includes an `opencode.json` that registers the Delphi LSP tool
147
205
  and model aliases for local Ollama and vLLM endpoints.
148
206
 
149
- For normal local opencode work, use the Ollama alias with a larger context:
207
+ To add the Delphi language server to another opencode project without the
208
+ agent tool, install the package in the Python environment used by opencode and
209
+ add an `lsp.delphi` entry to that project's `opencode.json`:
210
+
211
+ ```json
212
+ {
213
+ "$schema": "https://opencode.ai/config.json",
214
+ "lsp": {
215
+ "delphi": {
216
+ "command": ["python", "-m", "delphiast.lsp_server"],
217
+ "extensions": [".pas", ".dpr", ".dpk", ".inc"],
218
+ "initialization": {
219
+ "autoDiscoverPaths": true
220
+ }
221
+ }
222
+ }
223
+ }
224
+ ```
225
+
226
+ For checkout development, this repository's own `opencode.json` uses the local
227
+ virtual environment and sets `PYTHONPATH` so opencode loads the source tree
228
+ directly:
229
+
230
+ ```json
231
+ {
232
+ "lsp": {
233
+ "delphi": {
234
+ "command": [".venv/bin/python", "-m", "delphiast.lsp_server"],
235
+ "extensions": [".pas", ".dpr", ".dpk", ".inc"],
236
+ "env": {
237
+ "PYTHONPATH": "."
238
+ },
239
+ "initialization": {
240
+ "autoDiscoverPaths": true
241
+ }
242
+ }
243
+ }
244
+ }
245
+ ```
246
+
247
+ On Windows checkout development, replace `.venv/bin/python` with
248
+ `.venv\\Scripts\\python.exe`. If `python-delphi-lsp` is installed normally, the
249
+ portable `["python", "-m", "delphiast.lsp_server"]` command above works without
250
+ `PYTHONPATH`.
251
+
252
+ For normal local opencode work, use the Ollama alias with a larger context and
253
+ enable opencode's experimental LSP tool when starting opencode:
150
254
 
151
255
  ```bash
256
+ OPENCODE_EXPERIMENTAL_LSP_TOOL=true opencode run --dir . --model ollama/ornith-lspctx
257
+ ```
258
+
259
+ On Windows PowerShell:
260
+
261
+ ```powershell
262
+ $env:OPENCODE_EXPERIMENTAL_LSP_TOOL = "true"
152
263
  opencode run --dir . --model ollama/ornith-lspctx
153
264
  ```
154
265
 
@@ -244,6 +355,39 @@ The release evidence from the local proof recorded:
244
355
  - `context_budget.status = "pass"`
245
356
  - `goal_audit.status = "pass"`
246
357
 
358
+ ## Reproducing the vLLM opencode Skill Test
359
+
360
+ The codebase skill proof validates the `.agents` skill and opencode custom tool
361
+ instead of only the raw LSP tool. It creates a sandbox Delphi project with:
362
+
363
+ - `Main.dpr`
364
+ - `Main.dproj`
365
+ - `src/Mega100kUnit.pas` with more than 100,000 lines
366
+ - `include/build.inc`
367
+ - `.agents/skills/delphi-codebase-navigator/SKILL.md`
368
+ - `.opencode/tools/delphi_codebase.ts`
369
+
370
+ Run it against the local Ornith vLLM endpoint:
371
+
372
+ ```bash
373
+ python scripts/bootstrap_vllm_codebase_skill_test.py --use-running-server
374
+ ```
375
+
376
+ Or let the macOS helper start vLLM without downloading model shards:
377
+
378
+ ```bash
379
+ python scripts/bootstrap_vllm_codebase_skill_test.py --start-vllm
380
+ ```
381
+
382
+ This skill probe defaults the vLLM helper to `MAX_MODEL_LEN=32768`, which was
383
+ the stable local Metal setting for the complete skill + `delphi_codebase` tool
384
+ call. Override with `--max-model-len` when your machine has enough headroom.
385
+
386
+ The probe requires opencode to load `delphi-codebase-navigator`, call
387
+ `delphi_codebase`, and find `MegaProc02500`. It fails if opencode uses `bash`,
388
+ `read`, `glob`, `grep`, `edit`, `write`, `task`, `webfetch`, or `todowrite`
389
+ before the required evidence is complete.
390
+
247
391
  ## Verification
248
392
 
249
393
  Run the local test suite:
@@ -267,8 +411,8 @@ python -m twine check dist/*
267
411
 
268
412
  ## Repository Layout
269
413
 
270
- - `delphiast/` - parser, preprocessor, semantic model, workspace indexer, and
271
- LSP server
414
+ - `delphiast/` - parser, preprocessor, project discovery, semantic model,
415
+ workspace indexer, agent layers, and LSP server
272
416
  - `scripts/` - release evidence, cache checks, opencode probes, and bootstrap
273
417
  helpers
274
418
  - `tests/` - parser, semantic, workspace, diagnostics, packaging, and LSP tests
@@ -4,8 +4,9 @@ Python Delphi LSP is a standalone Python package for Delphi/Object Pascal
4
4
  parsing, semantic indexing, diagnostics, and Language Server Protocol support.
5
5
 
6
6
  The distributable package is named `python-delphi-lsp`. The import package keeps
7
- the established `delphiast` name, and the language-server executable is
8
- `delphi-lsp`.
7
+ the established `delphiast` name. The language-server executable is
8
+ `delphi-lsp`, and the agent-facing codebase navigator executable is
9
+ `delphi-lsp-agent`.
9
10
 
10
11
  ## What It Provides
11
12
 
@@ -17,7 +18,10 @@ the established `delphiast` name, and the language-server executable is
17
18
  - Workspace indexing across Delphi projects
18
19
  - LSP support for document symbols, workspace symbols, hover, definition,
19
20
  references, rename, completion, and diagnostics
20
- - opencode integration through the experimental LSP tool
21
+ - Automatic project path discovery from `.dpr`, `.dpk`, `.dproj`, `.cfg`, and
22
+ `.dof` files
23
+ - opencode integration through the experimental LSP tool and an installable
24
+ `.agents` skill with a dedicated codebase-inspection tool
21
25
 
22
26
  ## Installation
23
27
 
@@ -99,17 +103,124 @@ python -m delphiast.lsp_server
99
103
  ```
100
104
 
101
105
  The server expects normal LSP JSON-RPC over stdio. Editors and tools should set
102
- the workspace root to the Delphi project directory and pass any include paths or
103
- compiler defines through LSP initialization options when needed.
106
+ the workspace root to the Delphi project directory.
107
+
108
+ Auto-discovery reads `.dpr`, `.dpk`, `.dproj`, `.cfg`, and `.dof` files to
109
+ collect unit search paths, include paths, defines, direct `Unit in 'path.pas'`
110
+ references, and workspace source directories. Manual initialization options are
111
+ still accepted as overrides, but a normal project should not require users to
112
+ set include or source paths by hand.
113
+
114
+ ```json
115
+ {
116
+ "$schema": "https://opencode.ai/config.json",
117
+ "lsp": {
118
+ "delphi": {
119
+ "command": ["python", "-m", "delphiast.lsp_server"],
120
+ "extensions": [".pas", ".dpr", ".dpk", ".inc"],
121
+ "initialization": {
122
+ "autoDiscoverPaths": true
123
+ }
124
+ }
125
+ }
126
+ }
127
+ ```
128
+
129
+ ## Agent Codebase Navigator
130
+
131
+ Install the opencode skill and custom tool in a Delphi checkout:
132
+
133
+ ```bash
134
+ delphi-lsp-agent opencode install --target . --write-config
135
+ ```
136
+
137
+ This writes:
138
+
139
+ - `.agents/skills/delphi-codebase-navigator/SKILL.md`
140
+ - `.opencode/tools/delphi_codebase.ts`
141
+ - an optional `vllm-delphi-codebase` agent entry in `opencode.json`
142
+
143
+ The skill tells agents to inspect Delphi code through layered semantic views
144
+ instead of loading full source files. The custom opencode tool calls
145
+ `python -m delphiast.agent_cli` directly, so the agent does not need shell text
146
+ search or raw file-reading tools to understand the codebase.
147
+
148
+ Useful direct commands:
149
+
150
+ ```bash
151
+ delphi-lsp-agent view --root . --layer overview
152
+ delphi-lsp-agent view --root . --layer projects
153
+ delphi-lsp-agent view --root . --layer unit --query Mega100kUnit
154
+ delphi-lsp-agent view --root . --layer symbols --query TWorker --format json
155
+ delphi-lsp-agent view --root . --layer problems
156
+ ```
157
+
158
+ Available layers are `overview`, `projects`, `units`, `unit`, `symbols`,
159
+ `symbol`, `references`, and `problems`. The output includes file and line
160
+ citations, declarations, ownership, visibility, type information, and
161
+ dependency/problem summaries. Routine bodies are not emitted.
104
162
 
105
163
  ## opencode Usage
106
164
 
107
165
  This repository includes an `opencode.json` that registers the Delphi LSP tool
108
166
  and model aliases for local Ollama and vLLM endpoints.
109
167
 
110
- For normal local opencode work, use the Ollama alias with a larger context:
168
+ To add the Delphi language server to another opencode project without the
169
+ agent tool, install the package in the Python environment used by opencode and
170
+ add an `lsp.delphi` entry to that project's `opencode.json`:
171
+
172
+ ```json
173
+ {
174
+ "$schema": "https://opencode.ai/config.json",
175
+ "lsp": {
176
+ "delphi": {
177
+ "command": ["python", "-m", "delphiast.lsp_server"],
178
+ "extensions": [".pas", ".dpr", ".dpk", ".inc"],
179
+ "initialization": {
180
+ "autoDiscoverPaths": true
181
+ }
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ For checkout development, this repository's own `opencode.json` uses the local
188
+ virtual environment and sets `PYTHONPATH` so opencode loads the source tree
189
+ directly:
190
+
191
+ ```json
192
+ {
193
+ "lsp": {
194
+ "delphi": {
195
+ "command": [".venv/bin/python", "-m", "delphiast.lsp_server"],
196
+ "extensions": [".pas", ".dpr", ".dpk", ".inc"],
197
+ "env": {
198
+ "PYTHONPATH": "."
199
+ },
200
+ "initialization": {
201
+ "autoDiscoverPaths": true
202
+ }
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ On Windows checkout development, replace `.venv/bin/python` with
209
+ `.venv\\Scripts\\python.exe`. If `python-delphi-lsp` is installed normally, the
210
+ portable `["python", "-m", "delphiast.lsp_server"]` command above works without
211
+ `PYTHONPATH`.
212
+
213
+ For normal local opencode work, use the Ollama alias with a larger context and
214
+ enable opencode's experimental LSP tool when starting opencode:
111
215
 
112
216
  ```bash
217
+ OPENCODE_EXPERIMENTAL_LSP_TOOL=true opencode run --dir . --model ollama/ornith-lspctx
218
+ ```
219
+
220
+ On Windows PowerShell:
221
+
222
+ ```powershell
223
+ $env:OPENCODE_EXPERIMENTAL_LSP_TOOL = "true"
113
224
  opencode run --dir . --model ollama/ornith-lspctx
114
225
  ```
115
226
 
@@ -205,6 +316,39 @@ The release evidence from the local proof recorded:
205
316
  - `context_budget.status = "pass"`
206
317
  - `goal_audit.status = "pass"`
207
318
 
319
+ ## Reproducing the vLLM opencode Skill Test
320
+
321
+ The codebase skill proof validates the `.agents` skill and opencode custom tool
322
+ instead of only the raw LSP tool. It creates a sandbox Delphi project with:
323
+
324
+ - `Main.dpr`
325
+ - `Main.dproj`
326
+ - `src/Mega100kUnit.pas` with more than 100,000 lines
327
+ - `include/build.inc`
328
+ - `.agents/skills/delphi-codebase-navigator/SKILL.md`
329
+ - `.opencode/tools/delphi_codebase.ts`
330
+
331
+ Run it against the local Ornith vLLM endpoint:
332
+
333
+ ```bash
334
+ python scripts/bootstrap_vllm_codebase_skill_test.py --use-running-server
335
+ ```
336
+
337
+ Or let the macOS helper start vLLM without downloading model shards:
338
+
339
+ ```bash
340
+ python scripts/bootstrap_vllm_codebase_skill_test.py --start-vllm
341
+ ```
342
+
343
+ This skill probe defaults the vLLM helper to `MAX_MODEL_LEN=32768`, which was
344
+ the stable local Metal setting for the complete skill + `delphi_codebase` tool
345
+ call. Override with `--max-model-len` when your machine has enough headroom.
346
+
347
+ The probe requires opencode to load `delphi-codebase-navigator`, call
348
+ `delphi_codebase`, and find `MegaProc02500`. It fails if opencode uses `bash`,
349
+ `read`, `glob`, `grep`, `edit`, `write`, `task`, `webfetch`, or `todowrite`
350
+ before the required evidence is complete.
351
+
208
352
  ## Verification
209
353
 
210
354
  Run the local test suite:
@@ -228,8 +372,8 @@ python -m twine check dist/*
228
372
 
229
373
  ## Repository Layout
230
374
 
231
- - `delphiast/` - parser, preprocessor, semantic model, workspace indexer, and
232
- LSP server
375
+ - `delphiast/` - parser, preprocessor, project discovery, semantic model,
376
+ workspace indexer, agent layers, and LSP server
233
377
  - `scripts/` - release evidence, cache checks, opencode probes, and bootstrap
234
378
  helpers
235
379
  - `tests/` - parser, semantic, workspace, diagnostics, packaging, and LSP tests
@@ -46,6 +46,7 @@ from .semantic import (
46
46
  from .semantic_builder import SemanticBuilder, SemanticModel, SemanticProblem
47
47
  from .workspace import WorkspaceSemanticResult, build_workspace_semantics
48
48
  from .lsp_server import LspWorkspaceState, create_server
49
+ from .project_discovery import DelphiProjectDiscovery, DiscoveryProblem, discover_delphi_project
49
50
  from .project_indexer import (
50
51
  GetUnitSyntaxHook,
51
52
  IncludeFileInfo,
@@ -119,6 +120,9 @@ __all__ = [
119
120
  'build_workspace_semantics',
120
121
  'LspWorkspaceState',
121
122
  'create_server',
123
+ 'DelphiProjectDiscovery',
124
+ 'DiscoveryProblem',
125
+ 'discover_delphi_project',
122
126
  'ProjectIndexer',
123
127
  'ProjectIndexResult',
124
128
  'ProjectProblemType',
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ from .agent_layers import build_codebase_index, layer_payload, render_layer
9
+ from .agent_templates import install_opencode_support, install_skill
10
+
11
+
12
+ def build_parser() -> argparse.ArgumentParser:
13
+ parser = argparse.ArgumentParser(
14
+ prog="delphi-lsp-agent",
15
+ description="Agent-facing Delphi/Object Pascal codebase navigation helpers.",
16
+ )
17
+ subcommands = parser.add_subparsers(dest="command", required=True)
18
+
19
+ view = subcommands.add_parser("view", help="Render a layered codebase view.")
20
+ view.add_argument("--root", type=Path, default=Path("."))
21
+ view.add_argument("--project-file", type=Path)
22
+ view.add_argument("--layer", required=True, choices=["overview", "projects", "units", "unit", "symbols", "symbol", "references", "problems"])
23
+ view.add_argument("--query", default="")
24
+ view.add_argument("--format", default="markdown", choices=["markdown", "json"])
25
+ view.add_argument("--deep-projects", action="store_true", help="Deep-parse project dependencies for the projects layer.")
26
+ view.set_defaults(func=_view)
27
+
28
+ index = subcommands.add_parser("index", help="Materialize a JSON codebase index.")
29
+ index.add_argument("--root", type=Path, default=Path("."))
30
+ index.add_argument("--project-file", type=Path)
31
+ index.add_argument("--out", type=Path, default=Path(".delphi-lsp") / "agent-index" / "index.json")
32
+ index.set_defaults(func=_index)
33
+
34
+ skill = subcommands.add_parser("skill", help="Install agent skill templates.")
35
+ skill_commands = skill.add_subparsers(dest="skill_command", required=True)
36
+ skill_install = skill_commands.add_parser("install", help="Install .agents skill.")
37
+ skill_install.add_argument("--target", type=Path, default=Path("."))
38
+ skill_install.add_argument("--force", action="store_true")
39
+ skill_install.set_defaults(func=_skill_install)
40
+
41
+ opencode = subcommands.add_parser("opencode", help="Install opencode integration.")
42
+ opencode_commands = opencode.add_subparsers(dest="opencode_command", required=True)
43
+ opencode_install = opencode_commands.add_parser("install", help="Install .agents skill and opencode custom tool.")
44
+ opencode_install.add_argument("--target", type=Path, default=Path("."))
45
+ opencode_install.add_argument("--python", default=sys.executable)
46
+ opencode_install.add_argument("--force", action="store_true")
47
+ opencode_install.add_argument("--write-config", action="store_true")
48
+ opencode_install.set_defaults(func=_opencode_install)
49
+
50
+ return parser
51
+
52
+
53
+ def main(argv: list[str] | None = None) -> int:
54
+ parser = build_parser()
55
+ args = parser.parse_args(argv)
56
+ try:
57
+ args.func(args)
58
+ except BrokenPipeError:
59
+ return 1
60
+ return 0
61
+
62
+
63
+ def _view(args: argparse.Namespace) -> None:
64
+ index = build_codebase_index(args.root, project_file=args.project_file, index_projects=args.deep_projects)
65
+ sys.stdout.write(render_layer(index, args.layer, query=args.query, output_format=args.format))
66
+
67
+
68
+ def _index(args: argparse.Namespace) -> None:
69
+ index = build_codebase_index(args.root, project_file=args.project_file, index_projects=True)
70
+ payload = {
71
+ "overview": layer_payload(index, "overview"),
72
+ "projects": layer_payload(index, "projects"),
73
+ "problems": layer_payload(index, "problems"),
74
+ }
75
+ args.out.parent.mkdir(parents=True, exist_ok=True)
76
+ args.out.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
77
+ print(args.out)
78
+
79
+
80
+ def _skill_install(args: argparse.Namespace) -> None:
81
+ skill_path = install_skill(args.target, force=args.force)
82
+ print(skill_path)
83
+
84
+
85
+ def _opencode_install(args: argparse.Namespace) -> None:
86
+ skill_path, tool_path, config_path = install_opencode_support(
87
+ args.target,
88
+ python_executable=args.python,
89
+ force=args.force,
90
+ write_config=args.write_config,
91
+ )
92
+ print(skill_path)
93
+ print(tool_path)
94
+ if config_path is not None:
95
+ print(config_path)
96
+
97
+
98
+ if __name__ == "__main__":
99
+ raise SystemExit(main())