python-delphi-lsp 1.0.1__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.
Files changed (112) hide show
  1. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/MANIFEST.in +1 -0
  2. {python_delphi_lsp-1.0.1/python_delphi_lsp.egg-info → python_delphi_lsp-1.1.1}/PKG-INFO +117 -18
  3. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/README.md +116 -17
  4. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/__init__.py +4 -0
  5. python_delphi_lsp-1.1.1/delphiast/agent_cli.py +113 -0
  6. python_delphi_lsp-1.1.1/delphiast/agent_layers.py +497 -0
  7. python_delphi_lsp-1.1.1/delphiast/agent_templates.py +180 -0
  8. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/lsp_server.py +62 -3
  9. python_delphi_lsp-1.1.1/delphiast/project_discovery.py +375 -0
  10. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/opencode.json +1 -5
  11. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/pyproject.toml +2 -1
  12. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1/python_delphi_lsp.egg-info}/PKG-INFO +117 -18
  13. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/SOURCES.txt +8 -0
  14. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/entry_points.txt +1 -0
  15. python_delphi_lsp-1.1.1/scripts/bootstrap_vllm_codebase_skill_test.py +235 -0
  16. python_delphi_lsp-1.1.1/tests/test_agent_codebase.py +217 -0
  17. python_delphi_lsp-1.1.1/tests/test_bootstrap_vllm_codebase_skill.py +68 -0
  18. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_opencode_config.py +9 -0
  19. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_package_metadata.py +6 -2
  20. python_delphi_lsp-1.1.1/tests/test_project_discovery.py +174 -0
  21. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/LICENSE +0 -0
  22. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/binary.py +0 -0
  23. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/comment_builder.py +0 -0
  24. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/consts.py +0 -0
  25. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/grammar.py +0 -0
  26. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/lark_builder.py +0 -0
  27. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/lark_tokens.py +0 -0
  28. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/nodes.py +0 -0
  29. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/parser.py +0 -0
  30. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/preprocessor.py +0 -0
  31. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/project_indexer.py +0 -0
  32. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/semantic.py +0 -0
  33. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/semantic_builder.py +0 -0
  34. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/source_reader.py +0 -0
  35. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/workspace.py +0 -0
  36. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/delphiast/writer.py +0 -0
  37. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/dependency_links.txt +0 -0
  38. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/requires.txt +0 -0
  39. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/python_delphi_lsp.egg-info/top_level.txt +0 -0
  40. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/audit_delphi_language_features.py +0 -0
  41. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_opencode_test.ps1 +0 -0
  42. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_opencode_test.py +0 -0
  43. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/bootstrap_vllm_opencode_test.sh +0 -0
  44. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/check_ornith_cache.py +0 -0
  45. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/generate_release_evidence.py +0 -0
  46. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/ollama/ornith-lspctx.Modelfile +0 -0
  47. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/prepare_ornith_cache.py +0 -0
  48. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/run_opencode_lsp_probe.py +0 -0
  49. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/start_ornith_vllm.sh +0 -0
  50. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/scripts/summarize_opencode_request_payloads.py +0 -0
  51. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/setup.cfg +0 -0
  52. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/DeprecatedOnConst.pas +0 -0
  53. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/VariantRecordFieldAttributes.pas +0 -0
  54. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/alignedrecords.pas +0 -0
  55. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/constset.pas +0 -0
  56. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/deprecatedtype.pas +0 -0
  57. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/dottedtypes.pas +0 -0
  58. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/endtoken.pas +0 -0
  59. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/experimentals.pas +0 -0
  60. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/externalfunction.pas +0 -0
  61. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/finalizationinitializationexports.pas +0 -0
  62. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/forwardoverloaded.pas +0 -0
  63. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/forwardwithoutsemicolon.pas +0 -0
  64. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/genericconstraints.pas +0 -0
  65. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/genericinterfacemethoddelegation.pas +0 -0
  66. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/implementsgenerictype.pas +0 -0
  67. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/include file2.inc +0 -0
  68. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/includefile.inc +0 -0
  69. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/includefile.pas +0 -0
  70. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/managedrecords.pas +0 -0
  71. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/messagemethod.pas +0 -0
  72. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/multiline.pas +0 -0
  73. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/nonalignedrecords.pas +0 -0
  74. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/numbers.pas +0 -0
  75. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/pointerchars.pas +0 -0
  76. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/properties.pas +0 -0
  77. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/strictvisibility.pas +0 -0
  78. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/tryexcept.pas +0 -0
  79. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/umlauts.pas +0 -0
  80. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/legacy_snippets/whitespacearoundifdefcondition.pas +0 -0
  81. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/library_demo.dpr +0 -0
  82. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/package_demo.dpk +0 -0
  83. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/program_demo.dpr +0 -0
  84. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_advanced.pas +0 -0
  85. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_attributes.pas +0 -0
  86. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_basic.pas +0 -0
  87. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_consumer.pas +0 -0
  88. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_generics.pas +0 -0
  89. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_inheritance.pas +0 -0
  90. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_math.pas +0 -0
  91. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_properties.pas +0 -0
  92. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_sections.pas +0 -0
  93. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_statements.pas +0 -0
  94. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_types.pas +0 -0
  95. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_unresolved.pas +0 -0
  96. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/fixtures/unit_with.pas +0 -0
  97. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_bootstrap_vllm_opencode.py +0 -0
  98. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_diagnostics.py +0 -0
  99. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_language_feature_matrix.py +0 -0
  100. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_legacy_snippets.py +0 -0
  101. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_lsp_features.py +0 -0
  102. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_lsp_support.py +0 -0
  103. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_opencode_probe.py +0 -0
  104. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_opencode_request_payloads.py +0 -0
  105. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_ornith_cache_prepare.py +0 -0
  106. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_parser.py +0 -0
  107. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_preprocessor.py +0 -0
  108. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_project_indexer.py +0 -0
  109. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_release_evidence.py +0 -0
  110. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_semantic.py +0 -0
  111. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/tests/test_vllm_start_script.py +0 -0
  112. {python_delphi_lsp-1.0.1 → python_delphi_lsp-1.1.1}/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.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
@@ -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,77 @@ 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 implementation --query TWorker.Run
195
+ delphi-lsp-agent view --root . --layer implementation --query TWorker --format json
196
+ delphi-lsp-agent view --root . --layer problems
197
+ ```
198
+
199
+ Available layers are `overview`, `projects`, `units`, `unit`, `symbols`,
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.
143
207
 
144
208
  ## opencode Usage
145
209
 
146
210
  This repository includes an `opencode.json` that registers the Delphi LSP tool
147
211
  and model aliases for local Ollama and vLLM endpoints.
148
212
 
149
- To add the Delphi language server to another opencode project, install the
150
- package in the Python environment used by opencode and add an `lsp.delphi`
151
- entry to that project's `opencode.json`:
213
+ To add the Delphi language server to another opencode project without the
214
+ agent tool, install the package in the Python environment used by opencode and
215
+ add an `lsp.delphi` entry to that project's `opencode.json`:
152
216
 
153
217
  ```json
154
218
  {
@@ -158,17 +222,16 @@ entry to that project's `opencode.json`:
158
222
  "command": ["python", "-m", "delphiast.lsp_server"],
159
223
  "extensions": [".pas", ".dpr", ".dpk", ".inc"],
160
224
  "initialization": {
161
- "includePaths": ["src", "lib"],
162
- "defines": ["MSWINDOWS"]
225
+ "autoDiscoverPaths": true
163
226
  }
164
227
  }
165
228
  }
166
229
  }
167
230
  ```
168
231
 
169
- Adjust `includePaths` and `defines` to match the Delphi project. For checkout
170
- development, this repository's own `opencode.json` uses the local virtual
171
- environment and sets `PYTHONPATH` so opencode loads the source tree directly:
232
+ For checkout development, this repository's own `opencode.json` uses the local
233
+ virtual environment and sets `PYTHONPATH` so opencode loads the source tree
234
+ directly:
172
235
 
173
236
  ```json
174
237
  {
@@ -180,8 +243,7 @@ environment and sets `PYTHONPATH` so opencode loads the source tree directly:
180
243
  "PYTHONPATH": "."
181
244
  },
182
245
  "initialization": {
183
- "includePaths": ["tests/fixtures", "tests/fixtures/legacy_snippets"],
184
- "defines": []
246
+ "autoDiscoverPaths": true
185
247
  }
186
248
  }
187
249
  }
@@ -299,6 +361,43 @@ The release evidence from the local proof recorded:
299
361
  - `context_budget.status = "pass"`
300
362
  - `goal_audit.status = "pass"`
301
363
 
364
+ ## Reproducing the vLLM opencode Skill Test
365
+
366
+ The codebase skill proof validates the `.agents` skill and opencode custom tool
367
+ instead of only the raw LSP tool. It creates a sandbox Delphi project with:
368
+
369
+ - `Main.dpr`
370
+ - `Main.dproj`
371
+ - `src/Mega100kUnit.pas` with more than 100,000 lines
372
+ - `include/build.inc`
373
+ - `.agents/skills/delphi-codebase-navigator/SKILL.md`
374
+ - `.opencode/tools/delphi_codebase.ts`
375
+
376
+ Run it against the local Ornith vLLM endpoint:
377
+
378
+ ```bash
379
+ python scripts/bootstrap_vllm_codebase_skill_test.py --use-running-server
380
+ ```
381
+
382
+ Or let the macOS helper start vLLM without downloading model shards:
383
+
384
+ ```bash
385
+ python scripts/bootstrap_vllm_codebase_skill_test.py --start-vllm
386
+ ```
387
+
388
+ This skill probe defaults the vLLM helper to `MAX_MODEL_LEN=32768`, which was
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.
400
+
302
401
  ## Verification
303
402
 
304
403
  Run the local test suite:
@@ -322,8 +421,8 @@ python -m twine check dist/*
322
421
 
323
422
  ## Repository Layout
324
423
 
325
- - `delphiast/` - parser, preprocessor, semantic model, workspace indexer, and
326
- LSP server
424
+ - `delphiast/` - parser, preprocessor, project discovery, semantic model,
425
+ workspace indexer, agent layers, and LSP server
327
426
  - `scripts/` - release evidence, cache checks, opencode probes, and bootstrap
328
427
  helpers
329
428
  - `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,77 @@ 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 implementation --query TWorker.Run
156
+ delphi-lsp-agent view --root . --layer implementation --query TWorker --format json
157
+ delphi-lsp-agent view --root . --layer problems
158
+ ```
159
+
160
+ Available layers are `overview`, `projects`, `units`, `unit`, `symbols`,
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.
104
168
 
105
169
  ## opencode Usage
106
170
 
107
171
  This repository includes an `opencode.json` that registers the Delphi LSP tool
108
172
  and model aliases for local Ollama and vLLM endpoints.
109
173
 
110
- To add the Delphi language server to another opencode project, install the
111
- package in the Python environment used by opencode and add an `lsp.delphi`
112
- entry to that project's `opencode.json`:
174
+ To add the Delphi language server to another opencode project without the
175
+ agent tool, install the package in the Python environment used by opencode and
176
+ add an `lsp.delphi` entry to that project's `opencode.json`:
113
177
 
114
178
  ```json
115
179
  {
@@ -119,17 +183,16 @@ entry to that project's `opencode.json`:
119
183
  "command": ["python", "-m", "delphiast.lsp_server"],
120
184
  "extensions": [".pas", ".dpr", ".dpk", ".inc"],
121
185
  "initialization": {
122
- "includePaths": ["src", "lib"],
123
- "defines": ["MSWINDOWS"]
186
+ "autoDiscoverPaths": true
124
187
  }
125
188
  }
126
189
  }
127
190
  }
128
191
  ```
129
192
 
130
- Adjust `includePaths` and `defines` to match the Delphi project. For checkout
131
- development, this repository's own `opencode.json` uses the local virtual
132
- environment and sets `PYTHONPATH` so opencode loads the source tree directly:
193
+ For checkout development, this repository's own `opencode.json` uses the local
194
+ virtual environment and sets `PYTHONPATH` so opencode loads the source tree
195
+ directly:
133
196
 
134
197
  ```json
135
198
  {
@@ -141,8 +204,7 @@ environment and sets `PYTHONPATH` so opencode loads the source tree directly:
141
204
  "PYTHONPATH": "."
142
205
  },
143
206
  "initialization": {
144
- "includePaths": ["tests/fixtures", "tests/fixtures/legacy_snippets"],
145
- "defines": []
207
+ "autoDiscoverPaths": true
146
208
  }
147
209
  }
148
210
  }
@@ -260,6 +322,43 @@ The release evidence from the local proof recorded:
260
322
  - `context_budget.status = "pass"`
261
323
  - `goal_audit.status = "pass"`
262
324
 
325
+ ## Reproducing the vLLM opencode Skill Test
326
+
327
+ The codebase skill proof validates the `.agents` skill and opencode custom tool
328
+ instead of only the raw LSP tool. It creates a sandbox Delphi project with:
329
+
330
+ - `Main.dpr`
331
+ - `Main.dproj`
332
+ - `src/Mega100kUnit.pas` with more than 100,000 lines
333
+ - `include/build.inc`
334
+ - `.agents/skills/delphi-codebase-navigator/SKILL.md`
335
+ - `.opencode/tools/delphi_codebase.ts`
336
+
337
+ Run it against the local Ornith vLLM endpoint:
338
+
339
+ ```bash
340
+ python scripts/bootstrap_vllm_codebase_skill_test.py --use-running-server
341
+ ```
342
+
343
+ Or let the macOS helper start vLLM without downloading model shards:
344
+
345
+ ```bash
346
+ python scripts/bootstrap_vllm_codebase_skill_test.py --start-vllm
347
+ ```
348
+
349
+ This skill probe defaults the vLLM helper to `MAX_MODEL_LEN=32768`, which was
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.
361
+
263
362
  ## Verification
264
363
 
265
364
  Run the local test suite:
@@ -283,8 +382,8 @@ python -m twine check dist/*
283
382
 
284
383
  ## Repository Layout
285
384
 
286
- - `delphiast/` - parser, preprocessor, semantic model, workspace indexer, and
287
- LSP server
385
+ - `delphiast/` - parser, preprocessor, project discovery, semantic model,
386
+ workspace indexer, agent layers, and LSP server
288
387
  - `scripts/` - release evidence, cache checks, opencode probes, and bootstrap
289
388
  helpers
290
389
  - `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,113 @@
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(
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
+ )
37
+ view.add_argument("--query", default="")
38
+ view.add_argument("--format", default="markdown", choices=["markdown", "json"])
39
+ view.add_argument("--deep-projects", action="store_true", help="Deep-parse project dependencies for the projects layer.")
40
+ view.set_defaults(func=_view)
41
+
42
+ index = subcommands.add_parser("index", help="Materialize a JSON codebase index.")
43
+ index.add_argument("--root", type=Path, default=Path("."))
44
+ index.add_argument("--project-file", type=Path)
45
+ index.add_argument("--out", type=Path, default=Path(".delphi-lsp") / "agent-index" / "index.json")
46
+ index.set_defaults(func=_index)
47
+
48
+ skill = subcommands.add_parser("skill", help="Install agent skill templates.")
49
+ skill_commands = skill.add_subparsers(dest="skill_command", required=True)
50
+ skill_install = skill_commands.add_parser("install", help="Install .agents skill.")
51
+ skill_install.add_argument("--target", type=Path, default=Path("."))
52
+ skill_install.add_argument("--force", action="store_true")
53
+ skill_install.set_defaults(func=_skill_install)
54
+
55
+ opencode = subcommands.add_parser("opencode", help="Install opencode integration.")
56
+ opencode_commands = opencode.add_subparsers(dest="opencode_command", required=True)
57
+ opencode_install = opencode_commands.add_parser("install", help="Install .agents skill and opencode custom tool.")
58
+ opencode_install.add_argument("--target", type=Path, default=Path("."))
59
+ opencode_install.add_argument("--python", default=sys.executable)
60
+ opencode_install.add_argument("--force", action="store_true")
61
+ opencode_install.add_argument("--write-config", action="store_true")
62
+ opencode_install.set_defaults(func=_opencode_install)
63
+
64
+ return parser
65
+
66
+
67
+ def main(argv: list[str] | None = None) -> int:
68
+ parser = build_parser()
69
+ args = parser.parse_args(argv)
70
+ try:
71
+ args.func(args)
72
+ except BrokenPipeError:
73
+ return 1
74
+ return 0
75
+
76
+
77
+ def _view(args: argparse.Namespace) -> None:
78
+ index = build_codebase_index(args.root, project_file=args.project_file, index_projects=args.deep_projects)
79
+ sys.stdout.write(render_layer(index, args.layer, query=args.query, output_format=args.format))
80
+
81
+
82
+ def _index(args: argparse.Namespace) -> None:
83
+ index = build_codebase_index(args.root, project_file=args.project_file, index_projects=True)
84
+ payload = {
85
+ "overview": layer_payload(index, "overview"),
86
+ "projects": layer_payload(index, "projects"),
87
+ "problems": layer_payload(index, "problems"),
88
+ }
89
+ args.out.parent.mkdir(parents=True, exist_ok=True)
90
+ args.out.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
91
+ print(args.out)
92
+
93
+
94
+ def _skill_install(args: argparse.Namespace) -> None:
95
+ skill_path = install_skill(args.target, force=args.force)
96
+ print(skill_path)
97
+
98
+
99
+ def _opencode_install(args: argparse.Namespace) -> None:
100
+ skill_path, tool_path, config_path = install_opencode_support(
101
+ args.target,
102
+ python_executable=args.python,
103
+ force=args.force,
104
+ write_config=args.write_config,
105
+ )
106
+ print(skill_path)
107
+ print(tool_path)
108
+ if config_path is not None:
109
+ print(config_path)
110
+
111
+
112
+ if __name__ == "__main__":
113
+ raise SystemExit(main())